Spaces:
Running
Running
Mattthew
commited on
Commit
•
474474d
1
Parent(s):
b0f5ee6
major refactor - attempt to use data cache
Browse filesattempt to use data cache in place of localstorage when the latter is stupidly blocked by chrome. Since cache is async, that required a refactor. meanwhile I made some speed improvements
- index.html +1 -1
- index.js +371 -338
index.html
CHANGED
@@ -32,7 +32,7 @@
|
|
32 |
</ul>
|
33 |
<h3>Hide low-use tags</h3>
|
34 |
<ul>
|
35 |
-
<li><strong>checked:</strong> hides tags that match less than
|
36 |
<li>note that all hidden tags are unchecked</li>
|
37 |
</ul>
|
38 |
<h3>Hide unknown to SDXL</h3>
|
|
|
32 |
</ul>
|
33 |
<h3>Hide low-use tags</h3>
|
34 |
<ul>
|
35 |
+
<li><strong>checked:</strong> hides tags that match less than 4 artists, ~50% of all tags</li>
|
36 |
<li>note that all hidden tags are unchecked</li>
|
37 |
</ul>
|
38 |
<h3>Hide unknown to SDXL</h3>
|
index.js
CHANGED
@@ -15,81 +15,181 @@ var theTime = new Date;
|
|
15 |
var startUpTime;
|
16 |
var tagsConcatenated = new Set();
|
17 |
var editedArtists = new Set();
|
18 |
-
var
|
19 |
-
|
20 |
-
|
21 |
//
|
22 |
//
|
23 |
//
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
24 |
// functions
|
25 |
-
function startUp() {
|
26 |
-
checkLocalStorageAccess();
|
27 |
-
alertNoLocalStorage(2000);
|
28 |
-
checkCacheAccess();
|
29 |
updateTagsConcatenated();
|
30 |
updateFooter();
|
31 |
-
loadEditedArtists();
|
32 |
insertArtists();
|
33 |
insertCheckboxesFromArtistsData();
|
34 |
insertCheckboxesFromCategories();
|
35 |
-
loadCheckboxesState();
|
36 |
showHideCategories();
|
37 |
-
loadOptionsState();
|
38 |
-
loadFavoritesState();
|
39 |
hideAllArtists();
|
40 |
unhideBasedOnPermissiveSetting();
|
41 |
updateArtistsCountPerTag('start');
|
42 |
rotatePromptsImages();
|
43 |
sortArtists();
|
44 |
sortTags();
|
45 |
-
loadMostUsedTags();
|
46 |
updateArtistsCountPerCategory();
|
47 |
showHideLowCountTags();
|
48 |
makeStyleRuleForDrag();
|
49 |
teasePartition();
|
|
|
50 |
}
|
51 |
|
52 |
-
function
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
60 |
}
|
61 |
}
|
62 |
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
73 |
}
|
74 |
}
|
75 |
|
76 |
-
function
|
77 |
-
if(
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
msg += '3. Download the app to use offline\n\n';
|
84 |
-
msg += 'This app doesn\'t use cookies and instead saves all settings locally so that no data is ever sent to any server. But when you set to Chrome to block third-party cookies (which you should), it stupidly also blocks local storage. That\'s because Google wants you to feel pain for blocking their ad-based revenue model. To change this setting in Chrome (not recommended):\n';
|
85 |
-
msg += '1. In settings, click "Privacy and security"\n';
|
86 |
-
msg += '2. Click "Third-party cookies" and set it to "Allow third-party cookies"\n';
|
87 |
-
msg += '3. Unfortunately, that will allow all 3rd-party cookies on all sites, which is exactly what Google wants.\n';
|
88 |
-
alert(msg);
|
89 |
-
},wait);
|
90 |
}
|
91 |
}
|
92 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
93 |
function updateTagsConcatenated() {
|
94 |
// this set is used for tag editing mode
|
95 |
for (var i=0, il=tagCategories.length; i<il; i++) {
|
@@ -117,10 +217,9 @@ function updateFooter() {
|
|
117 |
}
|
118 |
}
|
119 |
|
120 |
-
function loadEditedArtists() {
|
121 |
-
|
122 |
-
|
123 |
-
editedArtists = new Set(arr);
|
124 |
let proto = window.location.protocol;
|
125 |
let anyChanges = false;
|
126 |
for (let i=0, il=artistsData.length; i<il; i++) {
|
@@ -147,9 +246,9 @@ function loadEditedArtists() {
|
|
147 |
}
|
148 |
}
|
149 |
if(anyChanges) {
|
150 |
-
|
151 |
}
|
152 |
-
}
|
153 |
}
|
154 |
|
155 |
function insertArtists() {
|
@@ -159,16 +258,13 @@ function insertArtists() {
|
|
159 |
let imagePromises = artistsData.map((artist) => {
|
160 |
var last = artist[0];
|
161 |
var first = artist[1];
|
162 |
-
var tags1 = artist[2].replaceAll('|', ' ').toLowerCase(); // for classes
|
163 |
var tags2 = artist[2].replaceAll('|', ', '); // for display
|
164 |
-
// class names can't start with a number, but some tags do
|
165 |
-
// in these cases we prepend the class with 'qqqq-'
|
166 |
-
tags1 = tags1.replace(/(^|\s)(\d)/g, '$1qqqq-$2');
|
167 |
// artists can have a tag in the format of "added-YYYY-MM"
|
168 |
// we want that to show up as a filter, but not on the artist card
|
169 |
tags2 = tags2.replace(/, added-(\d|-)*/g,'');
|
170 |
var itemDiv = document.createElement('div');
|
171 |
-
itemDiv.className = 'image-item
|
|
|
172 |
if(artist[3]) {
|
173 |
itemDiv.dataset.deprecated = true;
|
174 |
}
|
@@ -359,9 +455,8 @@ function insertCheckboxesFromCategories() {
|
|
359 |
}
|
360 |
}
|
361 |
|
362 |
-
function loadCheckboxesState() {
|
363 |
-
|
364 |
-
let state = JSON.parse(localStorage.getItem('tagsChecked')) || {};
|
365 |
let allChecked = true;
|
366 |
for (let name in state) {
|
367 |
if (document.querySelector('input[name="'+name+'"]')) {
|
@@ -376,39 +471,32 @@ function loadCheckboxesState() {
|
|
376 |
if(!allChecked) {
|
377 |
document.querySelector('input[name="check-all"]').checked = false;
|
378 |
}
|
379 |
-
}
|
380 |
}
|
381 |
|
382 |
function storeCheckboxState(checkbox) {
|
383 |
-
|
384 |
-
let state = JSON.parse(localStorage.getItem('tagsChecked')) || {};
|
385 |
-
state[checkbox.name] = checkbox.checked;
|
386 |
-
localStorage.setItem('tagsChecked', JSON.stringify(state));
|
387 |
-
}
|
388 |
}
|
389 |
|
390 |
function storeCheckboxStateAll(isChecked) {
|
391 |
-
|
392 |
-
|
393 |
-
|
394 |
-
|
395 |
-
|
396 |
-
|
397 |
-
|
398 |
-
|
399 |
-
|
400 |
-
|
401 |
-
state[checkbox.name] = false;
|
402 |
-
}
|
403 |
}
|
404 |
-
}
|
405 |
-
|
406 |
-
|
407 |
}
|
408 |
|
409 |
-
function loadOptionsState() {
|
410 |
-
|
411 |
-
let state = JSON.parse(localStorage.getItem('tagsChecked')) || {};
|
412 |
if(state['prompt']) {
|
413 |
document.getElementById('options_prompts').querySelectorAll('.selected')[0].classList.remove('selected');
|
414 |
document.getElementById(state['prompt']).classList.add('selected');
|
@@ -435,7 +523,7 @@ function loadOptionsState() {
|
|
435 |
} else {
|
436 |
// sortTC is already highlighted by HTML
|
437 |
}
|
438 |
-
}
|
439 |
}
|
440 |
|
441 |
function highlightSelectedOption(selected) {
|
@@ -481,27 +569,25 @@ function highlightSelectedOption(selected) {
|
|
481 |
}
|
482 |
|
483 |
function storeOptionsState() {
|
484 |
-
|
485 |
-
|
486 |
-
|
487 |
-
|
488 |
-
|
489 |
-
|
490 |
-
|
491 |
-
|
492 |
-
|
493 |
-
|
494 |
-
|
495 |
-
|
496 |
-
|
497 |
-
|
498 |
-
|
499 |
-
|
500 |
-
|
501 |
-
state['tagSort'] = 'sortTA';
|
502 |
-
}
|
503 |
-
localStorage.setItem('tagsChecked', JSON.stringify(state));
|
504 |
}
|
|
|
505 |
}
|
506 |
|
507 |
function rotatePromptsImages() {
|
@@ -538,72 +624,60 @@ function rotatePromptsImages() {
|
|
538 |
}
|
539 |
|
540 |
function updateArtistsCountPerTag(whoCalled) {
|
541 |
-
|
542 |
-
|
543 |
-
|
544 |
-
|
545 |
-
|
546 |
-
|
547 |
-
|
548 |
-
|
549 |
-
|
550 |
-
|
551 |
-
|
552 |
-
|
553 |
-
|
554 |
-
|
555 |
-
|
556 |
-
|
557 |
-
|
558 |
-
|
559 |
-
|
560 |
-
|
561 |
-
|
562 |
-
|
563 |
-
|
564 |
-
|
565 |
-
|
566 |
-
|
567 |
-
|
568 |
-
|
569 |
-
|
570 |
-
|
571 |
-
|
572 |
-
}
|
573 |
});
|
574 |
-
|
575 |
-
|
576 |
-
|
577 |
-
|
578 |
-
|
579 |
-
|
580 |
-
|
581 |
-
|
582 |
-
|
583 |
-
|
584 |
-
|
585 |
-
|
586 |
-
|
587 |
-
|
588 |
-
});
|
589 |
-
if(deprecatedCheckbox.checked) {
|
590 |
-
count = filteredDivs.length;
|
591 |
-
} else {
|
592 |
-
count = matchingDivs.length;
|
593 |
-
}
|
594 |
-
if(count == 0) {
|
595 |
-
checkbox.parentNode.classList.add('no_matches');
|
596 |
-
checkbox.parentNode.querySelector('input').disabled = true;
|
597 |
-
} else {
|
598 |
-
checkbox.parentNode.classList.remove('no_matches');
|
599 |
-
checkbox.parentNode.querySelector('input').disabled = false;
|
600 |
-
}
|
601 |
-
checkbox.parentNode.querySelector('.count').textContent = ' - ' + count.toLocaleString();
|
602 |
}
|
603 |
-
}
|
604 |
}
|
605 |
-
|
606 |
-
|
|
|
|
|
|
|
607 |
}
|
608 |
|
609 |
function updateArtistsCountPerCategory() {
|
@@ -613,14 +687,9 @@ function updateArtistsCountPerCategory() {
|
|
613 |
counts[i] = 0;
|
614 |
}
|
615 |
imageItems.forEach(function(imageItem) {
|
616 |
-
|
617 |
-
// class names can't start with a number,
|
618 |
-
// so some classes were prepending with 'qqqq-'
|
619 |
-
// which must be ignored
|
620 |
-
return className.replace(/^qqqq-/, '');
|
621 |
-
});
|
622 |
for(i=0,il=tagCategories.length; i<il; i++) {
|
623 |
-
if(tagCategories[i].map(
|
624 |
counts[i]++;
|
625 |
}
|
626 |
}
|
@@ -713,20 +782,18 @@ function unhideBasedOnPermissiveSetting() {
|
|
713 |
|
714 |
function unhideArtistsPermissive() {
|
715 |
// permissive mode unhides images that match ANY checked tag
|
716 |
-
// the set of checkboxes is derived from the unique tags within the imageItem (Artists)
|
717 |
var imageItems = document.querySelectorAll('.image-item');
|
718 |
var checkboxes = Array.from(document.querySelectorAll('input[type="checkbox"]'))
|
719 |
.filter(cb => !cb.parentNode.classList.contains("top_control"));
|
720 |
checkboxes.push(document.querySelector('input[name="favorite"]'));
|
721 |
var checked = checkboxes.filter(cb => cb.checked).map(cb => cb.name);
|
722 |
imageItems.forEach(function(imageItem) {
|
723 |
-
|
724 |
-
|
725 |
-
|
726 |
-
|
727 |
-
|
728 |
-
});
|
729 |
-
if(checked.some(c => classes.includes(c))) {
|
730 |
imageItem.classList.remove('hidden');
|
731 |
}
|
732 |
});
|
@@ -735,7 +802,7 @@ function unhideArtistsPermissive() {
|
|
735 |
|
736 |
function unhideArtistsStrict() {
|
737 |
// strict mode unhides images that match ALL checked tags
|
738 |
-
// the set of checkboxes is derived from the unique tags within the imageItem (Artists)
|
739 |
var imageItems = document.querySelectorAll('.image-item');
|
740 |
var checkboxes = Array.from(document.querySelectorAll('input[type="checkbox"]'))
|
741 |
.filter(cb => !cb.parentNode.classList.contains("top_control"));
|
@@ -743,13 +810,11 @@ function unhideArtistsStrict() {
|
|
743 |
var checked = checkboxes.filter(cb => cb.checked).map(cb => cb.name);
|
744 |
if(checked.length > 0) {
|
745 |
imageItems.forEach(function(imageItem, index) {
|
746 |
-
|
747 |
-
|
748 |
-
|
749 |
-
|
750 |
-
|
751 |
-
});
|
752 |
-
if(checked.every(c => classes.includes(c))) {
|
753 |
imageItem.classList.remove('hidden');
|
754 |
}
|
755 |
});
|
@@ -766,7 +831,7 @@ function unhideArtistsStrict() {
|
|
766 |
function unhideAristsExact() {
|
767 |
// exact mode isn't currently used because almost no two artists have the same set of tags
|
768 |
// exact mode unhides images that match ALL checked tags and NO unchecked tags
|
769 |
-
// the set of checkboxes is derived from the unique tags within the imageItem (Artists)
|
770 |
var imageItems = document.querySelectorAll('.image-item');
|
771 |
var checkboxes = Array.from(document.querySelectorAll('input[type="checkbox"]'))
|
772 |
.filter(cb => !cb.parentNode.classList.contains("top_control"));
|
@@ -775,9 +840,12 @@ function unhideAristsExact() {
|
|
775 |
var unchecked = checkboxes.filter(cb => !cb.checked).map(cb => cb.name);
|
776 |
if(checked.length > 0) {
|
777 |
imageItems.forEach(function(imageItem, index) {
|
778 |
-
|
779 |
-
if(
|
780 |
-
|
|
|
|
|
|
|
781 |
imageItem.classList.remove('hidden');
|
782 |
}
|
783 |
}
|
@@ -837,26 +905,25 @@ function showExport() {
|
|
837 |
// favorites
|
838 |
var textareaF = document.getElementById('export_favorites_list');
|
839 |
var favoritedArtists = false;
|
840 |
-
|
841 |
-
var favorites = localStorage.getItem('favoritedArtists');
|
842 |
var value = '';
|
843 |
-
if(
|
844 |
value += 'You have favorited these artists:\r\n';
|
845 |
-
for (let key in
|
846 |
-
if (
|
847 |
let names = key.split("|");
|
848 |
if(!names[0]) { names[0] = '(no first name)'; }
|
849 |
value += '•' + names[0] + ',' + names[1] + '\r\n';
|
850 |
}
|
851 |
}
|
852 |
-
value += '\r\n\r\nTo import these favorites later, click "copy to clipboard" and save to any file. Then paste the text from that file into this text box, and click "import". The imported text must contain the JSON string below (the curly brackets and what\'s between them). It must not contain any other more than one set of curly brackets.\r\n\r\n' +
|
853 |
textareaF.value = value;
|
854 |
} else {
|
855 |
value += 'You haven\'t favorited any artists yet.\r\n\r\n';
|
856 |
value += 'To import favorites that you exported earlier, paste the text into this text box, and click "import".';
|
857 |
textareaF.value = value;
|
858 |
}
|
859 |
-
}
|
860 |
// edits
|
861 |
var textareaE = document.getElementById('export_edits_list');
|
862 |
let editedArtistsArr = Array.from(editedArtists);
|
@@ -899,44 +966,42 @@ function exportTextarea(type) {
|
|
899 |
}
|
900 |
|
901 |
function importFavorites() {
|
902 |
-
|
903 |
-
|
904 |
-
|
905 |
-
|
906 |
-
|
907 |
-
|
908 |
-
|
909 |
-
|
910 |
-
|
911 |
-
|
912 |
-
|
913 |
-
|
914 |
-
|
915 |
-
|
916 |
-
|
917 |
-
|
918 |
-
|
919 |
-
|
920 |
-
|
921 |
-
|
922 |
-
|
923 |
-
|
924 |
-
el.value = 'That text can\'t be imported because the JSON string it contains doesn\'t contain a valid list of artists.'
|
925 |
-
return null;
|
926 |
-
}
|
927 |
-
}
|
928 |
-
if(confirm('This will overwrite any saved favorites. Are you sure?')) {
|
929 |
-
localStorage.setItem('favoritedArtists', jsonString);
|
930 |
-
alert('Favorites were imported!');
|
931 |
-
loadFavoritesState();
|
932 |
-
} else {
|
933 |
-
alert('Okay, you have cancelled the import.');
|
934 |
return null;
|
935 |
}
|
936 |
-
}
|
937 |
-
|
|
|
|
|
|
|
|
|
|
|
938 |
return null;
|
939 |
}
|
|
|
|
|
|
|
940 |
}
|
941 |
}
|
942 |
|
@@ -1112,9 +1177,8 @@ function sortTagsByCount() {
|
|
1112 |
}
|
1113 |
}
|
1114 |
|
1115 |
-
function loadMostUsedTags() {
|
1116 |
-
|
1117 |
-
let state = JSON.parse(localStorage.getItem('mustUsedTags')) || {};
|
1118 |
let mostUsedCategory = document.querySelector('[data-category-name="important"]');
|
1119 |
for(let tag in state) {
|
1120 |
if (state[tag]) {
|
@@ -1127,8 +1191,8 @@ function loadMostUsedTags() {
|
|
1127 |
updateTagArrayToMatchMostUsed(true,label,tag);
|
1128 |
}
|
1129 |
}
|
1130 |
-
}
|
1131 |
-
}
|
1132 |
}
|
1133 |
|
1134 |
function updateTagArrayToMatchMostUsed(isAdding,label,tag) {
|
@@ -1160,16 +1224,14 @@ function updateTagArrayToMatchMostUsed(isAdding,label,tag) {
|
|
1160 |
}
|
1161 |
|
1162 |
function storeMostUsedState(label) {
|
1163 |
-
|
1164 |
-
|
1165 |
-
let state = JSON.parse(localStorage.getItem('mustUsedTags')) || {};
|
1166 |
-
state[name] = label.classList.contains('is_most_used');
|
1167 |
-
localStorage.setItem('mustUsedTags', JSON.stringify(state));
|
1168 |
-
}
|
1169 |
}
|
1170 |
|
1171 |
function enterExitEditMostUsedMode(doExit) {
|
1172 |
-
if(
|
|
|
|
|
1173 |
let inputs = Array.from(document.querySelectorAll('input'));
|
1174 |
if(editMostUsedMode || doExit) {
|
1175 |
// exit edit mode
|
@@ -1200,8 +1262,6 @@ function enterExitEditMostUsedMode(doExit) {
|
|
1200 |
document.getElementById('gutter').style.left = '';
|
1201 |
document.getElementById('image-container').style.marginLeft = '';
|
1202 |
}
|
1203 |
-
} else {
|
1204 |
-
alertNoLocalStorage(0);
|
1205 |
}
|
1206 |
}
|
1207 |
|
@@ -1277,9 +1337,8 @@ function addOrRemoveFavorite(artist) {
|
|
1277 |
}
|
1278 |
}
|
1279 |
|
1280 |
-
function loadFavoritesState() {
|
1281 |
-
|
1282 |
-
let state = JSON.parse(localStorage.getItem('favoritedArtists')) || {};
|
1283 |
let artists = document.getElementsByClassName('image-item');
|
1284 |
for(let artist of artists) {
|
1285 |
let artistName = artist.getElementsByClassName('firstN')[0].textContent + '|' + artist.getElementsByClassName('lastN')[0].textContent;
|
@@ -1290,18 +1349,16 @@ function loadFavoritesState() {
|
|
1290 |
}
|
1291 |
}
|
1292 |
updateFavoritesCount();
|
1293 |
-
}
|
1294 |
}
|
1295 |
|
1296 |
function storeFavoriteState(artist) {
|
1297 |
-
if(
|
|
|
|
|
1298 |
var artistName = artist.getElementsByClassName('firstN')[0].textContent + '|' + artist.getElementsByClassName('lastN')[0].textContent;
|
1299 |
var isFavorited = artist.classList.contains('favorite');
|
1300 |
-
|
1301 |
-
state[artistName] = isFavorited;
|
1302 |
-
localStorage.setItem('favoritedArtists', JSON.stringify(state));
|
1303 |
-
} else {
|
1304 |
-
alertNoLocalStorage(0);
|
1305 |
}
|
1306 |
}
|
1307 |
|
@@ -1414,7 +1471,7 @@ function showHideLowCountTags() {
|
|
1414 |
// skip hide
|
1415 |
} else {
|
1416 |
let count = parseInt(checkbox.parentNode.querySelector('.count').textContent.replace(/,/g, '').trim().substring(2),10);
|
1417 |
-
if(count
|
1418 |
checkbox.checked = false;
|
1419 |
checkbox.parentNode.classList.add('hidden');
|
1420 |
}
|
@@ -1422,8 +1479,8 @@ function showHideLowCountTags() {
|
|
1422 |
} else {
|
1423 |
checkbox.parentNode.classList.remove('hidden');
|
1424 |
}
|
1425 |
-
showHideCategories();
|
1426 |
});
|
|
|
1427 |
}
|
1428 |
|
1429 |
function loadLargerImages(imageItem) {
|
@@ -1561,7 +1618,9 @@ function teasePartition() {
|
|
1561 |
}
|
1562 |
|
1563 |
function editTagsClicked(clickedImageItem) {
|
1564 |
-
if(
|
|
|
|
|
1565 |
let indicatorEl = clickedImageItem.querySelector('.art_edit span');
|
1566 |
if(indicatorEl.textContent == '✍️') {
|
1567 |
let artistWasInEditMode = editTagsFindArtistInEditMode(clickedImageItem);
|
@@ -1572,8 +1631,6 @@ function editTagsClicked(clickedImageItem) {
|
|
1572 |
} else {
|
1573 |
editTagsFindArtistInEditMode();
|
1574 |
}
|
1575 |
-
} else {
|
1576 |
-
alertNoLocalStorage(0);
|
1577 |
}
|
1578 |
}
|
1579 |
|
@@ -1765,66 +1822,64 @@ function focusInput(input) {
|
|
1765 |
}
|
1766 |
|
1767 |
function saveTagsForArtist(tagArea) {
|
1768 |
-
|
1769 |
-
|
1770 |
-
|
1771 |
-
|
1772 |
-
|
1773 |
-
|
1774 |
-
|
1775 |
-
|
1776 |
-
|
1777 |
-
|
1778 |
-
|
1779 |
-
newTagsArr.push(input.value);
|
1780 |
-
}
|
1781 |
}
|
1782 |
-
}
|
1783 |
-
|
1784 |
-
|
1785 |
-
|
1786 |
-
|
1787 |
-
|
1788 |
-
|
1789 |
-
|
1790 |
-
|
1791 |
-
|
1792 |
-
|
1793 |
-
|
1794 |
-
|
1795 |
-
|
1796 |
-
|
1797 |
-
|
1798 |
-
}
|
1799 |
-
let newTagsStr = newTagsArr.join('|');
|
1800 |
-
artist[2] = newTagsStr;
|
1801 |
-
// in db, true = hide unknown, but here true = known
|
1802 |
-
if(artistKnown) {
|
1803 |
-
artist[3] = false;
|
1804 |
-
} else {
|
1805 |
-
artist[3] = true;
|
1806 |
}
|
1807 |
-
edit = artist;
|
1808 |
-
break;
|
1809 |
}
|
1810 |
-
|
1811 |
-
|
1812 |
-
|
1813 |
-
|
1814 |
-
|
1815 |
-
|
|
|
1816 |
}
|
|
|
|
|
1817 |
}
|
1818 |
-
editedArtists.add(edit)
|
1819 |
-
// save edited artists locally
|
1820 |
-
localStorage.setItem('editedArtists', JSON.stringify(Array.from(editedArtists)));
|
1821 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1822 |
}
|
1823 |
|
1824 |
function deleteAllEdits() {
|
1825 |
-
if(
|
1826 |
if(confirm('This will delete all of your edits. Are you sure?')) {
|
1827 |
-
|
1828 |
alert('official database restored! this page will reload...');
|
1829 |
location.reload();
|
1830 |
} else {
|
@@ -1832,30 +1887,8 @@ function deleteAllEdits() {
|
|
1832 |
}
|
1833 |
}
|
1834 |
}
|
1835 |
-
|
1836 |
-
|
1837 |
-
//
|
1838 |
-
//
|
1839 |
-
//
|
1840 |
-
//
|
1841 |
-
//
|
1842 |
-
//
|
1843 |
-
//
|
1844 |
-
//
|
1845 |
-
//
|
1846 |
-
//
|
1847 |
-
//
|
1848 |
-
//
|
1849 |
-
//
|
1850 |
-
//
|
1851 |
-
// content loaded function
|
1852 |
-
document.addEventListener("DOMContentLoaded", function() {
|
1853 |
-
//
|
1854 |
-
//
|
1855 |
-
startUp();
|
1856 |
-
startUpTime = theTime.getTime();
|
1857 |
-
//
|
1858 |
-
//
|
1859 |
// add checkbox event listeners
|
1860 |
var checkboxes = document.querySelectorAll('input[type="checkbox"]');
|
1861 |
checkboxes.forEach(function(checkbox) {
|
@@ -2075,4 +2108,4 @@ document.addEventListener("DOMContentLoaded", function() {
|
|
2075 |
closeFooter.addEventListener('click', function(e) {
|
2076 |
document.getElementById('layout').classList.add('footerHidden');
|
2077 |
});
|
2078 |
-
}
|
|
|
15 |
var startUpTime;
|
16 |
var tagsConcatenated = new Set();
|
17 |
var editedArtists = new Set();
|
18 |
+
var storingAccessType = 'none';
|
19 |
+
const lowCountThreshold = 3;
|
|
|
20 |
//
|
21 |
//
|
22 |
//
|
23 |
+
// wait for DOM
|
24 |
+
document.addEventListener("DOMContentLoaded", function() {
|
25 |
+
checkStoringAccessType().then(state => {
|
26 |
+
startUp();
|
27 |
+
});
|
28 |
+
startUpTime = theTime.getTime();
|
29 |
+
});
|
30 |
+
|
31 |
// functions
|
32 |
+
async function startUp() {
|
|
|
|
|
|
|
33 |
updateTagsConcatenated();
|
34 |
updateFooter();
|
35 |
+
await loadEditedArtists();
|
36 |
insertArtists();
|
37 |
insertCheckboxesFromArtistsData();
|
38 |
insertCheckboxesFromCategories();
|
39 |
+
await loadCheckboxesState();
|
40 |
showHideCategories();
|
41 |
+
await loadOptionsState();
|
42 |
+
await loadFavoritesState();
|
43 |
hideAllArtists();
|
44 |
unhideBasedOnPermissiveSetting();
|
45 |
updateArtistsCountPerTag('start');
|
46 |
rotatePromptsImages();
|
47 |
sortArtists();
|
48 |
sortTags();
|
49 |
+
await loadMostUsedTags();
|
50 |
updateArtistsCountPerCategory();
|
51 |
showHideLowCountTags();
|
52 |
makeStyleRuleForDrag();
|
53 |
teasePartition();
|
54 |
+
addAllListeners();
|
55 |
}
|
56 |
|
57 |
+
function checkStoringAccessType() {
|
58 |
+
return new Promise((resolve, reject) => {
|
59 |
+
try {
|
60 |
+
localStorage.setItem('testKey', 'testValue');
|
61 |
+
localStorage.removeItem('testKey');
|
62 |
+
storingAccessType = 'localStorage';
|
63 |
+
console.log('all settings saved using localStorage');
|
64 |
+
resolve();
|
65 |
+
} catch (error) {
|
66 |
+
return caches.open('testCache')
|
67 |
+
.then(cache => {
|
68 |
+
const blob = new Blob([JSON.stringify('test')], { type: 'application/json' });
|
69 |
+
const responseToCache = new Response(blob);
|
70 |
+
cache.put('testCache', responseToCache).then(response => {
|
71 |
+
storingAccessType = 'dataCache';
|
72 |
+
console.log('all settings saved using dataCache');
|
73 |
+
return;
|
74 |
+
})
|
75 |
+
.catch(error => {
|
76 |
+
console.warn('no settings can be saved; we only have read access to cache: ' + error);
|
77 |
+
alertNoStoringAccess(2000);
|
78 |
+
resolve();
|
79 |
+
});
|
80 |
+
})
|
81 |
+
.catch(error => {
|
82 |
+
console.warn('no settings can be saved; no access to any storage method: ' + error);
|
83 |
+
alertNoStoringAccess(2000);
|
84 |
+
resolve();
|
85 |
+
});
|
86 |
+
}
|
87 |
+
}).catch(error => {
|
88 |
+
console.warn('had error writing to localStorage: ', error);
|
89 |
+
});
|
90 |
+
}
|
91 |
+
|
92 |
+
function loadItemBasedOnAccessType(item) {
|
93 |
+
if(storingAccessType == 'localStorage') {
|
94 |
+
return new Promise((resolve, reject) => {
|
95 |
+
try {
|
96 |
+
const state = JSON.parse(localStorage.getItem(item));
|
97 |
+
resolve(state || {});
|
98 |
+
} catch (error) {
|
99 |
+
reject(error);
|
100 |
+
}
|
101 |
+
}).catch(error => {
|
102 |
+
console.warn(item + ' had error loading from localStorage: ', error);
|
103 |
+
return {};
|
104 |
+
});
|
105 |
+
} else if(storingAccessType == 'dataCache') {
|
106 |
+
return caches.open('dataCache')
|
107 |
+
.then(cache => {
|
108 |
+
return cache.match(item);
|
109 |
+
})
|
110 |
+
.then(response => {
|
111 |
+
if(response) {
|
112 |
+
return response.json();
|
113 |
+
}
|
114 |
+
return {};
|
115 |
+
})
|
116 |
+
.catch(error => {
|
117 |
+
console.warn(item + ' had error loading from cache: ', error);
|
118 |
+
});
|
119 |
+
} else if(storingAccessType == 'none') {
|
120 |
+
return Promise.resolve({});
|
121 |
}
|
122 |
}
|
123 |
|
124 |
+
function storeItemBasedOnAccessType(item, stateArray, key, value) {
|
125 |
+
if(storingAccessType == 'localStorage') {
|
126 |
+
try {
|
127 |
+
if(stateArray) {
|
128 |
+
localStorage.setItem(item, JSON.stringify(stateArray));
|
129 |
+
} else {
|
130 |
+
let state = JSON.parse(localStorage.getItem(item)) || {};
|
131 |
+
state[key] = value;
|
132 |
+
localStorage.setItem(item, JSON.stringify(state));
|
133 |
+
}
|
134 |
+
} catch (error) {
|
135 |
+
console.warn(item + ' had error saving localStorage: ', error);
|
136 |
+
}
|
137 |
+
} else if(storingAccessType = 'dataCache') {
|
138 |
+
caches.open('dataCache').then(cache => {
|
139 |
+
if(stateArray) {
|
140 |
+
const blob = new Blob([JSON.stringify(stateArray)], { type: 'application/json' });
|
141 |
+
const responseToCache = new Response(blob);
|
142 |
+
return cache.put(item, responseToCache);
|
143 |
+
} else {
|
144 |
+
// try to get the item state from the cache
|
145 |
+
cache.match(item).then(response => {
|
146 |
+
let state = {};
|
147 |
+
if(response) {
|
148 |
+
return response.json().then(cachedData => {
|
149 |
+
state = cachedData || {};
|
150 |
+
return state;
|
151 |
+
});
|
152 |
+
} else {
|
153 |
+
return state;
|
154 |
+
}
|
155 |
+
}).then(state => {
|
156 |
+
state[key] = value;
|
157 |
+
// store the updated state back to the cache
|
158 |
+
const blob = new Blob([JSON.stringify(state)], { type: 'application/json' });
|
159 |
+
const responseToCache = new Response(blob);
|
160 |
+
return cache.put(item, responseToCache);
|
161 |
+
});
|
162 |
+
}
|
163 |
+
}).catch(error => {
|
164 |
+
console.warn(item + ' had error saving to cache: ', error);
|
165 |
+
});
|
166 |
+
} else if(storingAccessType == 'none') {
|
167 |
+
alertNoStoringAccess(0);
|
168 |
}
|
169 |
}
|
170 |
|
171 |
+
async function deleteItemBasedOnAccessType(item) {
|
172 |
+
if(storingAccessType == 'localStorage') {
|
173 |
+
localStorage.removeItem(item);
|
174 |
+
} else if(storingAccessType = 'dataCache') {
|
175 |
+
await caches.delete(item);
|
176 |
+
} else if(storingAccessType == 'none') {
|
177 |
+
// nothing to do
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
178 |
}
|
179 |
}
|
180 |
|
181 |
+
function alertNoStoringAccess(wait) {
|
182 |
+
window.setTimeout(function(){
|
183 |
+
let msg = '';
|
184 |
+
msg += 'My apologies, your browser settings block the ability to save settings and favorites. This was working on Firefox, Safari, and Chrome as of Sep. 2023. Suggestions:\n';
|
185 |
+
msg += '1. Try a different browser\n'
|
186 |
+
msg += '2. Check browser privacy settings\n';
|
187 |
+
msg += '3. Download the app to use offline\n\n';
|
188 |
+
msg += 'This app doesn\'t use cookies, saves all settings locally, and never sends data to any server. However, some privacy settings may block all data storage.'
|
189 |
+
alert(msg);
|
190 |
+
},wait);
|
191 |
+
}
|
192 |
+
|
193 |
function updateTagsConcatenated() {
|
194 |
// this set is used for tag editing mode
|
195 |
for (var i=0, il=tagCategories.length; i<il; i++) {
|
|
|
217 |
}
|
218 |
}
|
219 |
|
220 |
+
async function loadEditedArtists() {
|
221 |
+
await loadItemBasedOnAccessType('editedArtists').then(state => {
|
222 |
+
editedArtists = new Set(Array.from(state));
|
|
|
223 |
let proto = window.location.protocol;
|
224 |
let anyChanges = false;
|
225 |
for (let i=0, il=artistsData.length; i<il; i++) {
|
|
|
246 |
}
|
247 |
}
|
248 |
if(anyChanges) {
|
249 |
+
storeItemBasedOnAccessType('editedArtists',editedArtists,false,false);
|
250 |
}
|
251 |
+
});
|
252 |
}
|
253 |
|
254 |
function insertArtists() {
|
|
|
258 |
let imagePromises = artistsData.map((artist) => {
|
259 |
var last = artist[0];
|
260 |
var first = artist[1];
|
|
|
261 |
var tags2 = artist[2].replaceAll('|', ', '); // for display
|
|
|
|
|
|
|
262 |
// artists can have a tag in the format of "added-YYYY-MM"
|
263 |
// we want that to show up as a filter, but not on the artist card
|
264 |
tags2 = tags2.replace(/, added-(\d|-)*/g,'');
|
265 |
var itemDiv = document.createElement('div');
|
266 |
+
itemDiv.className = 'image-item';
|
267 |
+
itemDiv.dataset.tagList = artist[2].toLowerCase();
|
268 |
if(artist[3]) {
|
269 |
itemDiv.dataset.deprecated = true;
|
270 |
}
|
|
|
455 |
}
|
456 |
}
|
457 |
|
458 |
+
async function loadCheckboxesState() {
|
459 |
+
await loadItemBasedOnAccessType('tagsChecked').then(state => {
|
|
|
460 |
let allChecked = true;
|
461 |
for (let name in state) {
|
462 |
if (document.querySelector('input[name="'+name+'"]')) {
|
|
|
471 |
if(!allChecked) {
|
472 |
document.querySelector('input[name="check-all"]').checked = false;
|
473 |
}
|
474 |
+
});
|
475 |
}
|
476 |
|
477 |
function storeCheckboxState(checkbox) {
|
478 |
+
storeItemBasedOnAccessType('tagsChecked',false,checkbox.name,checkbox.checked);
|
|
|
|
|
|
|
|
|
479 |
}
|
480 |
|
481 |
function storeCheckboxStateAll(isChecked) {
|
482 |
+
let checkboxes = document.querySelectorAll('input[type="checkbox"]');
|
483 |
+
let state = {};
|
484 |
+
checkboxes.forEach(function(checkbox) {
|
485 |
+
let isTop = checkbox.parentNode.classList.contains('top_control');
|
486 |
+
if(!isTop || checkbox.name == 'favorite') {
|
487 |
+
// is a tag checkbox, not a setting
|
488 |
+
if(isChecked) {
|
489 |
+
state[checkbox.name] = true;
|
490 |
+
} else {
|
491 |
+
state[checkbox.name] = false;
|
|
|
|
|
492 |
}
|
493 |
+
}
|
494 |
+
});
|
495 |
+
storeItemBasedOnAccessType('tagsChecked',state,false,false);
|
496 |
}
|
497 |
|
498 |
+
async function loadOptionsState() {
|
499 |
+
await loadItemBasedOnAccessType('optionsChecked').then(state => {
|
|
|
500 |
if(state['prompt']) {
|
501 |
document.getElementById('options_prompts').querySelectorAll('.selected')[0].classList.remove('selected');
|
502 |
document.getElementById(state['prompt']).classList.add('selected');
|
|
|
523 |
} else {
|
524 |
// sortTC is already highlighted by HTML
|
525 |
}
|
526 |
+
});
|
527 |
}
|
528 |
|
529 |
function highlightSelectedOption(selected) {
|
|
|
569 |
}
|
570 |
|
571 |
function storeOptionsState() {
|
572 |
+
let state = {};
|
573 |
+
if(document.getElementById('promptA').classList.contains('selected')) {
|
574 |
+
state['prompt'] = 'promptA';
|
575 |
+
} else if(document.getElementById('promptP').classList.contains('selected')) {
|
576 |
+
state['prompt'] = 'promptP';
|
577 |
+
} else {
|
578 |
+
state['prompt'] = 'promptL';
|
579 |
+
}
|
580 |
+
if(document.getElementById('sortAR').classList.contains('selected')) {
|
581 |
+
state['artistSort'] = 'sortAR';
|
582 |
+
} else {
|
583 |
+
state['artistSort'] = 'sortAA';
|
584 |
+
}
|
585 |
+
if(document.getElementById('sortTC').classList.contains('selected')) {
|
586 |
+
state['tagSort'] = 'sortTC';
|
587 |
+
} else {
|
588 |
+
state['tagSort'] = 'sortTA';
|
|
|
|
|
|
|
589 |
}
|
590 |
+
storeItemBasedOnAccessType('optionsChecked',state,false,false);
|
591 |
}
|
592 |
|
593 |
function rotatePromptsImages() {
|
|
|
624 |
}
|
625 |
|
626 |
function updateArtistsCountPerTag(whoCalled) {
|
627 |
+
if(whoCalled == 'start') {
|
628 |
+
// on page load, we need to add all the counts first
|
629 |
+
updateArtistsCountPerTagSlow();
|
630 |
+
}
|
631 |
+
timer = setTimeout(function() {
|
632 |
+
// for checkbox, we defer counts because it's slow
|
633 |
+
updateArtistsCountPerTagSlow();
|
634 |
+
},0);
|
635 |
+
}
|
636 |
+
|
637 |
+
function updateArtistsCountPerTagSlow() {
|
638 |
+
let permissiveCheckbox = document.querySelector('input[name="mode"]');
|
639 |
+
let isPermissive = permissiveCheckbox.checked;
|
640 |
+
let deprecatedCheckbox = document.querySelector('input[name="deprecated"]');
|
641 |
+
let checkboxes = document.querySelectorAll('input[type="checkbox"]');
|
642 |
+
let divs = document.querySelectorAll('.image-item');
|
643 |
+
let hiddenDivs = document.querySelectorAll('.image-item.hidden');
|
644 |
+
let deprecatedDivs = document.querySelectorAll('.image-item[data-deprecated="true"]');
|
645 |
+
checkboxes.forEach(function(checkbox) {
|
646 |
+
let isTop = checkbox.parentNode.classList.contains('top_control');
|
647 |
+
if(!isTop) {
|
648 |
+
let matchingDivs;
|
649 |
+
if(isPermissive) {
|
650 |
+
matchingDivs = document.querySelectorAll('.image-item[data-tag-list*="' + checkbox.name + '"]');
|
651 |
+
} else {
|
652 |
+
// for strict mode, for each checkbox, only count artists with a tags matching all checked checkboxes
|
653 |
+
matchingDivs = document.querySelectorAll('.image-item[data-tag-list*="' + checkbox.name + '"]:not(.hidden)');
|
654 |
+
}
|
655 |
+
let filteredDivs = Array.from(matchingDivs).filter(mat => {
|
656 |
+
// only includes the artists known to SD
|
657 |
+
return !Array.from(deprecatedDivs).some(dep => dep === mat);
|
|
|
658 |
});
|
659 |
+
let count = 0;
|
660 |
+
if(deprecatedCheckbox.checked) {
|
661 |
+
count = filteredDivs.length;
|
662 |
+
} else {
|
663 |
+
count = matchingDivs.length;
|
664 |
+
}
|
665 |
+
if(!count) { count = 0; }
|
666 |
+
checkbox.parentNode.querySelector('.count').textContent = ' - ' + count.toLocaleString();
|
667 |
+
checkbox.parentNode.classList.remove('no_matches');
|
668 |
+
checkbox.parentNode.querySelector('input').disabled = false;
|
669 |
+
if(!isPermissive) {
|
670 |
+
if(count == 0) {
|
671 |
+
checkbox.parentNode.classList.add('no_matches');
|
672 |
+
checkbox.parentNode.querySelector('input').disabled = true;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
673 |
}
|
674 |
+
}
|
675 |
}
|
676 |
+
});
|
677 |
+
updateCountOfAllArtistsShown(divs, hiddenDivs);
|
678 |
+
if(isPermissive) {
|
679 |
+
updateArtistsCountPerCategory();
|
680 |
+
}
|
681 |
}
|
682 |
|
683 |
function updateArtistsCountPerCategory() {
|
|
|
687 |
counts[i] = 0;
|
688 |
}
|
689 |
imageItems.forEach(function(imageItem) {
|
690 |
+
let tagList = imageItem.dataset.tagList.split('|');
|
|
|
|
|
|
|
|
|
|
|
691 |
for(i=0,il=tagCategories.length; i<il; i++) {
|
692 |
+
if(tagCategories[i].map(tag => tag.toLowerCase()).some(tag => tagList.includes(tag))) {
|
693 |
counts[i]++;
|
694 |
}
|
695 |
}
|
|
|
782 |
|
783 |
function unhideArtistsPermissive() {
|
784 |
// permissive mode unhides images that match ANY checked tag
|
785 |
+
// the set of checkboxes is derived from the unique tags within the imageItem (Artists) tagList dataSet
|
786 |
var imageItems = document.querySelectorAll('.image-item');
|
787 |
var checkboxes = Array.from(document.querySelectorAll('input[type="checkbox"]'))
|
788 |
.filter(cb => !cb.parentNode.classList.contains("top_control"));
|
789 |
checkboxes.push(document.querySelector('input[name="favorite"]'));
|
790 |
var checked = checkboxes.filter(cb => cb.checked).map(cb => cb.name);
|
791 |
imageItems.forEach(function(imageItem) {
|
792 |
+
let tagList = imageItem.dataset.tagList.split('|');
|
793 |
+
if(imageItem.classList.contains('favorite')) {
|
794 |
+
tagList.push('favorite');
|
795 |
+
}
|
796 |
+
if(checked.some(tag => tagList.includes(tag))) {
|
|
|
|
|
797 |
imageItem.classList.remove('hidden');
|
798 |
}
|
799 |
});
|
|
|
802 |
|
803 |
function unhideArtistsStrict() {
|
804 |
// strict mode unhides images that match ALL checked tags
|
805 |
+
// the set of checkboxes is derived from the unique tags within the imageItem (Artists) tagList dataSet
|
806 |
var imageItems = document.querySelectorAll('.image-item');
|
807 |
var checkboxes = Array.from(document.querySelectorAll('input[type="checkbox"]'))
|
808 |
.filter(cb => !cb.parentNode.classList.contains("top_control"));
|
|
|
810 |
var checked = checkboxes.filter(cb => cb.checked).map(cb => cb.name);
|
811 |
if(checked.length > 0) {
|
812 |
imageItems.forEach(function(imageItem, index) {
|
813 |
+
let tagList = imageItem.dataset.tagList.split('|');
|
814 |
+
if(imageItem.classList.contains('favorite')) {
|
815 |
+
tagList.push('favorite');
|
816 |
+
}
|
817 |
+
if(checked.every(tag => tagList.includes(tag))) {
|
|
|
|
|
818 |
imageItem.classList.remove('hidden');
|
819 |
}
|
820 |
});
|
|
|
831 |
function unhideAristsExact() {
|
832 |
// exact mode isn't currently used because almost no two artists have the same set of tags
|
833 |
// exact mode unhides images that match ALL checked tags and NO unchecked tags
|
834 |
+
// the set of checkboxes is derived from the unique tags within the imageItem (Artists) tagList dataSet
|
835 |
var imageItems = document.querySelectorAll('.image-item');
|
836 |
var checkboxes = Array.from(document.querySelectorAll('input[type="checkbox"]'))
|
837 |
.filter(cb => !cb.parentNode.classList.contains("top_control"));
|
|
|
840 |
var unchecked = checkboxes.filter(cb => !cb.checked).map(cb => cb.name);
|
841 |
if(checked.length > 0) {
|
842 |
imageItems.forEach(function(imageItem, index) {
|
843 |
+
let tagList = imageItem.dataset.tagList.split('|');
|
844 |
+
if(imageItem.classList.contains('favorite')) {
|
845 |
+
tagList.push('favorite');
|
846 |
+
}
|
847 |
+
if(checked.every(tag => tagList.includes(tag))) {
|
848 |
+
if(unchecked.every(tag => !tagList.includes(tag))) {
|
849 |
imageItem.classList.remove('hidden');
|
850 |
}
|
851 |
}
|
|
|
905 |
// favorites
|
906 |
var textareaF = document.getElementById('export_favorites_list');
|
907 |
var favoritedArtists = false;
|
908 |
+
loadItemBasedOnAccessType('favoritedArtists').then(state => {
|
|
|
909 |
var value = '';
|
910 |
+
if(state) {
|
911 |
value += 'You have favorited these artists:\r\n';
|
912 |
+
for (let key in state) {
|
913 |
+
if (state[key] === true) {
|
914 |
let names = key.split("|");
|
915 |
if(!names[0]) { names[0] = '(no first name)'; }
|
916 |
value += '•' + names[0] + ',' + names[1] + '\r\n';
|
917 |
}
|
918 |
}
|
919 |
+
value += '\r\n\r\nTo import these favorites later, click "copy to clipboard" and save to any file. Then paste the text from that file into this text box, and click "import". The imported text must contain the JSON string below (the curly brackets and what\'s between them). It must not contain any other more than one set of curly brackets.\r\n\r\n' + state;
|
920 |
textareaF.value = value;
|
921 |
} else {
|
922 |
value += 'You haven\'t favorited any artists yet.\r\n\r\n';
|
923 |
value += 'To import favorites that you exported earlier, paste the text into this text box, and click "import".';
|
924 |
textareaF.value = value;
|
925 |
}
|
926 |
+
});
|
927 |
// edits
|
928 |
var textareaE = document.getElementById('export_edits_list');
|
929 |
let editedArtistsArr = Array.from(editedArtists);
|
|
|
966 |
}
|
967 |
|
968 |
function importFavorites() {
|
969 |
+
let el = document.getElementById('export_favorites_list');
|
970 |
+
let favorites = el.value;
|
971 |
+
let startCount = (favorites.match(/{/g) || []).length;
|
972 |
+
let endCount = (favorites.match(/}/g) || []).length;
|
973 |
+
if (startCount > 1 || endCount > 1) {
|
974 |
+
el.value = 'That text can\'t be imported because it contains multiple curly brackets {}.'
|
975 |
+
return null;
|
976 |
+
}
|
977 |
+
let start = favorites.indexOf('{');
|
978 |
+
let end = favorites.lastIndexOf('}');
|
979 |
+
if (start === -1 || end === -1) {
|
980 |
+
el.value = 'That text can\'t be imported because it contains zero curly brackets {}.'
|
981 |
+
return null;
|
982 |
+
}
|
983 |
+
let jsonString = favorites.substring(start, end + 1);
|
984 |
+
try {
|
985 |
+
let favoritesObject = JSON.parse(jsonString);
|
986 |
+
// check structure of each key-value pair in favoritesObject
|
987 |
+
for (let key in favoritesObject) {
|
988 |
+
let value = favoritesObject[key];
|
989 |
+
if (!key.includes('|') || typeof value !== 'boolean') {
|
990 |
+
el.value = 'That text can\'t be imported because the JSON string it contains doesn\'t contain a valid list of artists.'
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
991 |
return null;
|
992 |
}
|
993 |
+
}
|
994 |
+
if(confirm('This will overwrite any saved favorites. Are you sure?')) {
|
995 |
+
storeItemBasedOnAccessType('favoritedArtists',favoritesObject,false,false);
|
996 |
+
alert('Favorites were imported!');
|
997 |
+
loadFavoritesState();
|
998 |
+
} else {
|
999 |
+
alert('Okay, you have cancelled the import.');
|
1000 |
return null;
|
1001 |
}
|
1002 |
+
} catch(e) {
|
1003 |
+
el.value = 'That text can\'t be imported because it doesn\'t contain a valid JSON sting.'
|
1004 |
+
return null;
|
1005 |
}
|
1006 |
}
|
1007 |
|
|
|
1177 |
}
|
1178 |
}
|
1179 |
|
1180 |
+
async function loadMostUsedTags() {
|
1181 |
+
await loadItemBasedOnAccessType('mustUsedTags').then(state => {
|
|
|
1182 |
let mostUsedCategory = document.querySelector('[data-category-name="important"]');
|
1183 |
for(let tag in state) {
|
1184 |
if (state[tag]) {
|
|
|
1191 |
updateTagArrayToMatchMostUsed(true,label,tag);
|
1192 |
}
|
1193 |
}
|
1194 |
+
}
|
1195 |
+
});
|
1196 |
}
|
1197 |
|
1198 |
function updateTagArrayToMatchMostUsed(isAdding,label,tag) {
|
|
|
1224 |
}
|
1225 |
|
1226 |
function storeMostUsedState(label) {
|
1227 |
+
var name = label.querySelector('input').name;
|
1228 |
+
storeItemBasedOnAccessType('mustUsedTags',false,name,label.classList.contains('is_most_used'));
|
|
|
|
|
|
|
|
|
1229 |
}
|
1230 |
|
1231 |
function enterExitEditMostUsedMode(doExit) {
|
1232 |
+
if(storingAccessType == 'none') {
|
1233 |
+
alertNoStoringAccess(0)
|
1234 |
+
} else {
|
1235 |
let inputs = Array.from(document.querySelectorAll('input'));
|
1236 |
if(editMostUsedMode || doExit) {
|
1237 |
// exit edit mode
|
|
|
1262 |
document.getElementById('gutter').style.left = '';
|
1263 |
document.getElementById('image-container').style.marginLeft = '';
|
1264 |
}
|
|
|
|
|
1265 |
}
|
1266 |
}
|
1267 |
|
|
|
1337 |
}
|
1338 |
}
|
1339 |
|
1340 |
+
async function loadFavoritesState() {
|
1341 |
+
await loadItemBasedOnAccessType('favoritedArtists').then(state => {
|
|
|
1342 |
let artists = document.getElementsByClassName('image-item');
|
1343 |
for(let artist of artists) {
|
1344 |
let artistName = artist.getElementsByClassName('firstN')[0].textContent + '|' + artist.getElementsByClassName('lastN')[0].textContent;
|
|
|
1349 |
}
|
1350 |
}
|
1351 |
updateFavoritesCount();
|
1352 |
+
});
|
1353 |
}
|
1354 |
|
1355 |
function storeFavoriteState(artist) {
|
1356 |
+
if(storingAccessType == 'none') {
|
1357 |
+
alertNoStoringAccess(0)
|
1358 |
+
} else {
|
1359 |
var artistName = artist.getElementsByClassName('firstN')[0].textContent + '|' + artist.getElementsByClassName('lastN')[0].textContent;
|
1360 |
var isFavorited = artist.classList.contains('favorite');
|
1361 |
+
storeItemBasedOnAccessType('favoritedArtists',false,artistName,isFavorited);
|
|
|
|
|
|
|
|
|
1362 |
}
|
1363 |
}
|
1364 |
|
|
|
1471 |
// skip hide
|
1472 |
} else {
|
1473 |
let count = parseInt(checkbox.parentNode.querySelector('.count').textContent.replace(/,/g, '').trim().substring(2),10);
|
1474 |
+
if(count <= lowCountThreshold) {
|
1475 |
checkbox.checked = false;
|
1476 |
checkbox.parentNode.classList.add('hidden');
|
1477 |
}
|
|
|
1479 |
} else {
|
1480 |
checkbox.parentNode.classList.remove('hidden');
|
1481 |
}
|
|
|
1482 |
});
|
1483 |
+
showHideCategories();
|
1484 |
}
|
1485 |
|
1486 |
function loadLargerImages(imageItem) {
|
|
|
1618 |
}
|
1619 |
|
1620 |
function editTagsClicked(clickedImageItem) {
|
1621 |
+
if(storingAccessType == 'none') {
|
1622 |
+
alertNoStoringAccess(0);
|
1623 |
+
} else {
|
1624 |
let indicatorEl = clickedImageItem.querySelector('.art_edit span');
|
1625 |
if(indicatorEl.textContent == '✍️') {
|
1626 |
let artistWasInEditMode = editTagsFindArtistInEditMode(clickedImageItem);
|
|
|
1631 |
} else {
|
1632 |
editTagsFindArtistInEditMode();
|
1633 |
}
|
|
|
|
|
1634 |
}
|
1635 |
}
|
1636 |
|
|
|
1822 |
}
|
1823 |
|
1824 |
function saveTagsForArtist(tagArea) {
|
1825 |
+
// get new tags
|
1826 |
+
let tagLabels = tagArea.querySelectorAll('label');
|
1827 |
+
let newTagsArr = [];
|
1828 |
+
let artistKnown = true;
|
1829 |
+
tagLabels.forEach(function(label) {
|
1830 |
+
let input = label.querySelector('input');
|
1831 |
+
if(input.value == 'known') {
|
1832 |
+
artistKnown = input.checked;
|
1833 |
+
} else {
|
1834 |
+
if(input.checked) {
|
1835 |
+
newTagsArr.push(input.value);
|
|
|
|
|
1836 |
}
|
1837 |
+
}
|
1838 |
+
});
|
1839 |
+
// find match in artistsData
|
1840 |
+
let firstN = tagArea.closest('.image-item').querySelector('.firstN').textContent;
|
1841 |
+
let lastN = tagArea.closest('.image-item').querySelector('.lastN').textContent;
|
1842 |
+
let edit = [];
|
1843 |
+
for (let i=0, il=artistsData.length; i<il; i++) {
|
1844 |
+
let artist = artistsData[i];
|
1845 |
+
if(artist[0] == lastN && artist[1] == firstN) {
|
1846 |
+
// artists can have a tag in the format of "added-YYYY-MM-DD"
|
1847 |
+
// this was stripped earlier, so we need to add it back in
|
1848 |
+
let oldTagsArr = artist[2].split('|');
|
1849 |
+
for (let j=oldTagsArr.length-1; j>=0; j--) {
|
1850 |
+
// loop backwards because it should be at the end
|
1851 |
+
if(oldTagsArr[j].match(/added-(\d|-)*/)) {
|
1852 |
+
newTagsArr.push(oldTagsArr[j]);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1853 |
}
|
|
|
|
|
1854 |
}
|
1855 |
+
let newTagsStr = newTagsArr.join('|');
|
1856 |
+
artist[2] = newTagsStr;
|
1857 |
+
// in db, true = hide unknown, but here true = known
|
1858 |
+
if(artistKnown) {
|
1859 |
+
artist[3] = false;
|
1860 |
+
} else {
|
1861 |
+
artist[3] = true;
|
1862 |
}
|
1863 |
+
edit = artist;
|
1864 |
+
break;
|
1865 |
}
|
|
|
|
|
|
|
1866 |
}
|
1867 |
+
// replace old edits with new edits
|
1868 |
+
for (let i=0, il=editedArtists.length; i<il; i++) {
|
1869 |
+
let oldEdit = editedArtists[i];
|
1870 |
+
if(edit[0] == oldEdit[0] && edit[1] == oldEdit[1]) {
|
1871 |
+
editedArtists.delete(oldEdit);
|
1872 |
+
}
|
1873 |
+
}
|
1874 |
+
editedArtists.add(edit)
|
1875 |
+
// save edited artists locally
|
1876 |
+
storeItemBasedOnAccessType('editedArtists',Array.from(editedArtists),false,false);
|
1877 |
}
|
1878 |
|
1879 |
function deleteAllEdits() {
|
1880 |
+
if(storingAccessType != 'none') {
|
1881 |
if(confirm('This will delete all of your edits. Are you sure?')) {
|
1882 |
+
deleteItemBasedOnAccessType('editedArtists');
|
1883 |
alert('official database restored! this page will reload...');
|
1884 |
location.reload();
|
1885 |
} else {
|
|
|
1887 |
}
|
1888 |
}
|
1889 |
}
|
1890 |
+
|
1891 |
+
function addAllListeners() {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1892 |
// add checkbox event listeners
|
1893 |
var checkboxes = document.querySelectorAll('input[type="checkbox"]');
|
1894 |
checkboxes.forEach(function(checkbox) {
|
|
|
2108 |
closeFooter.addEventListener('click', function(e) {
|
2109 |
document.getElementById('layout').classList.add('footerHidden');
|
2110 |
});
|
2111 |
+
}
|