Mattthew commited on
Commit
a4898fa
1 Parent(s): 0c16b46

fixing bug for chrome security settings

Browse files

Chrome is stupid. No way to save beyond the session, but at leas the app works now

Files changed (1) hide show
  1. index.js +330 -258
index.js CHANGED
@@ -15,11 +15,13 @@ var theTime = new Date;
15
  var startUpTime;
16
  var tagsConcatenated = new Set();
17
  var editedArtists = new Set();
 
18
  //
19
  //
20
  //
21
  // functions
22
  function startUp() {
 
23
  updateTagsConcatenated();
24
  updateFooter();
25
  loadEditedArtists();
@@ -41,6 +43,36 @@ function startUp() {
41
  showHideLowCountTags();
42
  makeStyleRuleForDrag();
43
  teasePartition();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44
  }
45
 
46
  function updateTagsConcatenated() {
@@ -71,35 +103,37 @@ function updateFooter() {
71
  }
72
 
73
  function loadEditedArtists() {
74
- const arr = JSON.parse(localStorage.getItem('editedArtists')) || [];
75
- editedArtists = new Set(arr);
76
- let proto = window.location.protocol;
77
- let anyChanges = false;
78
- for (let i=0, il=artistsData.length; i<il; i++) {
79
- // find match in artistsData if first and last names match
80
- let artist = artistsData[i];
81
- let artistFound = Array.from(editedArtists).find(editedA => editedA[0] === artist[0] && editedA[1] === artist[1]);
82
- if(artistFound) {
83
- // check if the edit now matches the original
84
- let match = true;
85
- for (let j=0, jl=artist.length; j<jl; j++) {
86
- if (artist[j] !== artistFound[j]) {
87
- match = false;
 
 
88
  }
89
- }
90
- if(match) {
91
- anyChanges = true;
92
- editedArtists.delete(artistFound);
93
- } else {
94
- if (!proto.startsWith('http')) {
95
- // if this is a local file, then update artistData with the saved edits
96
- artistsData[i] = artistFound;
97
  }
98
  }
99
  }
100
- }
101
- if(anyChanges) {
102
- localStorage.setItem('editedArtists', JSON.stringify(Array.from(editedArtists)));
103
  }
104
  }
105
 
@@ -311,73 +345,81 @@ function insertCheckboxesFromCategories() {
311
  }
312
 
313
  function loadCheckboxesState() {
314
- let state = JSON.parse(localStorage.getItem('tagsChecked')) || {};
315
- let allChecked = true;
316
- for (let name in state) {
317
- if (document.querySelector('input[name="'+name+'"]')) {
318
- document.querySelector('input[name="'+name+'"]').checked = state[name];
319
- if(name != 'mode' && name != 'use_categories') {
320
- if(!state[name]) {
321
- allChecked = false;
 
 
322
  }
323
  }
324
  }
325
- }
326
- if(!allChecked) {
327
- document.querySelector('input[name="check-all"]').checked = false;
328
  }
329
  }
330
 
331
  function storeCheckboxState(checkbox) {
332
- let state = JSON.parse(localStorage.getItem('tagsChecked')) || {};
333
- state[checkbox.name] = checkbox.checked;
334
- localStorage.setItem('tagsChecked', JSON.stringify(state));
 
 
335
  }
336
 
337
  function storeCheckboxStateAll(isChecked) {
338
- let state = {};
339
- var checkboxes = document.querySelectorAll('input[type="checkbox"]');
340
- checkboxes.forEach(function(checkbox) {
341
- let isTop = checkbox.parentNode.classList.contains('top_control');
342
- if(!isTop || checkbox.name == 'favorite') {
343
- // is a tag checkbox, not a setting
344
- if(isChecked) {
345
- state[checkbox.name] = true;
346
- } else {
347
- state[checkbox.name] = false;
 
 
348
  }
349
- }
350
- });
351
- localStorage.setItem('tagsChecked', JSON.stringify(state));
352
  }
353
 
354
  function loadOptionsState() {
355
- let state = JSON.parse(localStorage.getItem('tagsChecked')) || {};
356
- if(state['prompt']) {
357
- document.getElementById('options_prompts').querySelectorAll('.selected')[0].classList.remove('selected');
358
- document.getElementById(state['prompt']).classList.add('selected');
359
- if(state['prompt'] == 'promptA') {
 
 
 
 
 
 
 
 
 
360
  imgTypeShown = 0;
361
- } else if(state['prompt'] == 'promptP') {
362
- imgTypeShown = 1;
363
- } else if(state['prompt'] == 'promptL') {
364
- imgTypeShown = 2;
365
  }
366
- } else {
367
- // promptA is already highlighted by HTML
368
- imgTypeShown = 0;
369
- }
370
- if(state['artistSort']) {
371
- document.getElementById('options_artist_sort').querySelectorAll('.selected')[0].classList.remove('selected');
372
- document.getElementById(state['artistSort']).classList.add('selected');
373
- } else {
374
- // sortAR is already highlighted by HTML
375
- }
376
- if(state['tagSort']) {
377
- document.getElementById('options_tag_sort').querySelectorAll('.selected')[0].classList.remove('selected');
378
- document.getElementById(state['tagSort']).classList.add('selected');
379
- } else {
380
- // sortTC is already highlighted by HTML
381
  }
382
  }
383
 
@@ -424,25 +466,27 @@ function highlightSelectedOption(selected) {
424
  }
425
 
426
  function storeOptionsState() {
427
- let state = JSON.parse(localStorage.getItem('tagsChecked')) || {};
428
- if(document.getElementById('promptA').classList.contains('selected')) {
429
- state['prompt'] = 'promptA';
430
- } else if(document.getElementById('promptP').classList.contains('selected')) {
431
- state['prompt'] = 'promptP';
432
- } else {
433
- state['prompt'] = 'promptL';
434
- }
435
- if(document.getElementById('sortAR').classList.contains('selected')) {
436
- state['artistSort'] = 'sortAR';
437
- } else {
438
- state['artistSort'] = 'sortAA';
439
- }
440
- if(document.getElementById('sortTC').classList.contains('selected')) {
441
- state['tagSort'] = 'sortTC';
442
- } else {
443
- state['tagSort'] = 'sortTA';
 
 
 
444
  }
445
- localStorage.setItem('tagsChecked', JSON.stringify(state));
446
  }
447
 
448
  function rotatePromptsImages() {
@@ -777,22 +821,26 @@ function showExport() {
777
  document.getElementById('export').classList.add('shown');
778
  // favorites
779
  var textareaF = document.getElementById('export_favorites_list');
780
- var favorites = localStorage.getItem('favoritedArtists');
781
- var value = '';
782
- if(favorites) {
783
- value += 'You have favorited these artists:\r\n';
784
- for (let key in JSON.parse(favorites)) {
785
- if (JSON.parse(favorites)[key] === true) {
786
- let names = key.split("|");
787
- if(!names[0]) { names[0] = '(no first name)'; }
788
- value += '•' + names[0] + ',' + names[1] + '\r\n';
 
 
 
789
  }
 
 
 
 
 
 
790
  }
791
- 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' + favorites;
792
- textareaF.value = value;
793
- } else {
794
- value += 'You haven\'t favorited any artists yet.\r\n\r\n';
795
- value += 'To import favorites that you exported earlier, paste the text into this text box, and click "import".';
796
  }
797
  // edits
798
  var textareaE = document.getElementById('export_edits_list');
@@ -836,42 +884,44 @@ function exportTextarea(type) {
836
  }
837
 
838
  function importFavorites() {
839
- let el = document.getElementById('export_favorites_list');
840
- let favorites = el.value;
841
- let startCount = (favorites.match(/{/g) || []).length;
842
- let endCount = (favorites.match(/}/g) || []).length;
843
- if (startCount > 1 || endCount > 1) {
844
- el.value = 'That text can\'t be imported because it contains multiple curly brackets {}.'
845
- return null;
846
- }
847
- let start = favorites.indexOf('{');
848
- let end = favorites.lastIndexOf('}');
849
- if (start === -1 || end === -1) {
850
- el.value = 'That text can\'t be imported because it contains zero curly brackets {}.'
851
- return null;
852
- }
853
- let jsonString = favorites.substring(start, end + 1);
854
- try {
855
- let jsonObject = JSON.parse(jsonString);
856
- // Check structure of each key-value pair in jsonObject
857
- for (let key in jsonObject) {
858
- let value = jsonObject[key];
859
- if (!key.includes('|') || typeof value !== 'boolean') {
860
- el.value = 'That text can\'t be imported because the JSON string it contains doesn\'t contain a valid list of artists.'
 
 
 
 
 
 
 
 
 
 
861
  return null;
862
  }
863
- }
864
- if(confirm('This will overwrite any saved favorites. Are you sure?')) {
865
- localStorage.setItem('favoritedArtists', jsonString);
866
- alert('Favorites were imported!');
867
- loadFavoritesState();
868
- } else {
869
- alert('Okay, you have cancelled the import.');
870
  return null;
871
  }
872
- } catch(e) {
873
- el.value = 'That text can\'t be imported because it doesn\'t contain a valid JSON sting.'
874
- return null;
875
  }
876
  }
877
 
@@ -1048,20 +1098,22 @@ function sortTagsByCount() {
1048
  }
1049
 
1050
  function loadMostUsedTags() {
1051
- let state = JSON.parse(localStorage.getItem('mustUsedTags')) || {};
1052
- let mostUsedCategory = document.querySelector('[data-category-name="important"]');
1053
- for(let tag in state) {
1054
- if (state[tag]) {
1055
- let label = document.querySelector('input[name="'+ tag +'"]');
1056
- if(label) {
1057
- label = label.parentNode;
1058
- label.classList.add('is_most_used');
1059
- label.querySelectorAll('.most_used_indicator')[0].textContent = '-';
1060
- mostUsedCategory.after(label);
1061
- updateTagArrayToMatchMostUsed(true,label,tag);
 
 
1062
  }
1063
- }
1064
- };
1065
  }
1066
 
1067
  function updateTagArrayToMatchMostUsed(isAdding,label,tag) {
@@ -1093,42 +1145,48 @@ function updateTagArrayToMatchMostUsed(isAdding,label,tag) {
1093
  }
1094
 
1095
  function storeMostUsedState(label) {
1096
- var name = label.querySelector('input').name;
1097
- let state = JSON.parse(localStorage.getItem('mustUsedTags')) || {};
1098
- state[name] = label.classList.contains('is_most_used');
1099
- localStorage.setItem('mustUsedTags', JSON.stringify(state));
 
 
1100
  }
1101
 
1102
  function enterExitEditMostUsedMode(doExit) {
1103
- let inputs = Array.from(document.querySelectorAll('input'));
1104
- if(editMostUsedMode || doExit) {
1105
- // exit edit mode
1106
- editMostUsedMode = false;
1107
- document.getElementById('edit_most_used').textContent = 'edit';
1108
- document.getElementById('layout').classList.remove('edit_mode');
1109
- inputs.forEach(function(input) {
1110
- input.disabled = false;
1111
- });
1112
- let labels = Array.from(document.querySelectorAll('.was_moved'));
1113
- labels.forEach(function(label) {
1114
- // clean up classes added to track moved tags during edit mode
1115
- label.classList.remove('was_moved');
1116
- })
1117
- document.getElementById('toggles').style.width = 'calc(' + gutterEndPercentX + '% - 20px)';
1118
- document.getElementById('gutter').style.left = gutterEndPercentX + '%';
1119
- document.getElementById('image-container').style.marginLeft = 'calc(' + gutterEndPercentX + '% + 50px)';
1120
- updateArtistsCountPerCategory();
 
 
 
 
 
 
 
 
 
 
 
 
 
1121
  } else {
1122
- // enter edit mode
1123
- editMostUsedMode = true;
1124
- document.getElementById('edit_most_used').textContent = 'exit editing';
1125
- document.getElementById('layout').classList.add('edit_mode');
1126
- inputs.forEach(function(input) {
1127
- input.disabled = true;
1128
- });
1129
- document.getElementById('toggles').style.width = '';
1130
- document.getElementById('gutter').style.left = '';
1131
- document.getElementById('image-container').style.marginLeft = '';
1132
  }
1133
  }
1134
 
@@ -1205,25 +1263,31 @@ function addOrRemoveFavorite(artist) {
1205
  }
1206
 
1207
  function loadFavoritesState() {
1208
- let state = JSON.parse(localStorage.getItem('favoritedArtists')) || {};
1209
- let artists = document.getElementsByClassName('image-item');
1210
- for(let artist of artists) {
1211
- let artistName = artist.getElementsByClassName('firstN')[0].textContent + '|' + artist.getElementsByClassName('lastN')[0].textContent;
1212
- if(state[artistName]) {
1213
- artist.classList.add('favorite');
1214
- } else {
1215
- artist.classList.remove('favorite');
 
 
1216
  }
 
1217
  }
1218
- updateFavoritesCount();
1219
  }
1220
 
1221
  function storeFavoriteState(artist) {
1222
- var artistName = artist.getElementsByClassName('firstN')[0].textContent + '|' + artist.getElementsByClassName('lastN')[0].textContent;
1223
- var isFavorited = artist.classList.contains('favorite');
1224
- let state = JSON.parse(localStorage.getItem('favoritedArtists')) || {};
1225
- state[artistName] = isFavorited;
1226
- localStorage.setItem('favoritedArtists', JSON.stringify(state));
 
 
 
 
1227
  }
1228
 
1229
  function updateFavoritesCount() {
@@ -1482,15 +1546,19 @@ function teasePartition() {
1482
  }
1483
 
1484
  function editTagsClicked(clickedImageItem) {
1485
- let indicatorEl = clickedImageItem.querySelector('.art_edit span');
1486
- if(indicatorEl.textContent == '✍️') {
1487
- let artistWasInEditMode = editTagsFindArtistInEditMode(clickedImageItem);
1488
- if(!artistWasInEditMode) {
1489
- doAlert('Read help ⁉️ first',1);
 
 
 
 
 
1490
  }
1491
- editTagsEnterEditMode(clickedImageItem);
1492
  } else {
1493
- editTagsFindArtistInEditMode();
1494
  }
1495
  }
1496
 
@@ -1679,67 +1747,71 @@ function focusInput(input) {
1679
  }
1680
 
1681
  function saveTagsForArtist(tagArea) {
1682
- // get new tags
1683
- let tagLabels = tagArea.querySelectorAll('label');
1684
- let newTagsArr = [];
1685
- let artistKnown = true;
1686
- tagLabels.forEach(function(label) {
1687
- let input = label.querySelector('input');
1688
- if(input.value == 'known') {
1689
- artistKnown = input.checked;
1690
- } else {
1691
- if(input.checked) {
1692
- newTagsArr.push(input.value);
1693
- }
1694
- }
1695
- });
1696
- // find match in artistsData
1697
- let firstN = tagArea.closest('.image-item').querySelector('.firstN').textContent;
1698
- let lastN = tagArea.closest('.image-item').querySelector('.lastN').textContent;
1699
- let edit = [];
1700
- for (let i=0, il=artistsData.length; i<il; i++) {
1701
- let artist = artistsData[i];
1702
- if(artist[0] == lastN && artist[1] == firstN) {
1703
- // artists can have a tag in the format of "added-YYYY-MM-DD"
1704
- // this was stripped earlier, so we need to add it back in
1705
- let oldTagsArr = artist[2].split('|');
1706
- for (let j=oldTagsArr.length-1; j>=0; j--) {
1707
- // loop backwards because it should be at the end
1708
- if(oldTagsArr[j].match(/added-(\d|-)*/)) {
1709
- newTagsArr.push(oldTagsArr[j]);
1710
  }
1711
  }
1712
- let newTagsStr = newTagsArr.join('|');
1713
- artist[2] = newTagsStr;
1714
- // in db, true = hide unknown, but here true = known
1715
- if(artistKnown) {
1716
- artist[3] = false;
1717
- } else {
1718
- artist[3] = true;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1719
  }
1720
- edit = artist;
1721
- break;
1722
  }
1723
- }
1724
- // replace old edits with new edits
1725
- for (let i=0, il=editedArtists.length; i<il; i++) {
1726
- let oldEdit = editedArtists[i];
1727
- if(edit[0] == oldEdit[0] && edit[1] == oldEdit[1]) {
1728
- editedArtists.delete(oldEdit);
1729
  }
 
 
 
1730
  }
1731
- editedArtists.add(edit)
1732
- // save edited artists locally
1733
- localStorage.setItem('editedArtists', JSON.stringify(Array.from(editedArtists)));
1734
  }
1735
 
1736
  function deleteAllEdits() {
1737
- if(confirm('This will delete all of your edits. Are you sure?')) {
1738
- localStorage.removeItem('editedArtists');
1739
- alert('official database restored! this page will reload...');
1740
- location.reload();
1741
- } else {
1742
- alert('restore was cancelled!');
 
 
1743
  }
1744
  }
1745
  //
 
15
  var startUpTime;
16
  var tagsConcatenated = new Set();
17
  var editedArtists = new Set();
18
+ var localStorageAccess = false;
19
  //
20
  //
21
  //
22
  // functions
23
  function startUp() {
24
+ checkLocalStorageAccess();
25
  updateTagsConcatenated();
26
  updateFooter();
27
  loadEditedArtists();
 
43
  showHideLowCountTags();
44
  makeStyleRuleForDrag();
45
  teasePartition();
46
+ localStorageAccess = false;
47
+ alertNoLocalStorage(2000);
48
+ }
49
+
50
+ function checkLocalStorageAccess() {
51
+ try {
52
+ localStorage.setItem('testKey', 'testValue');
53
+ localStorage.removeItem('testKey');
54
+ localStorageAccess = true;
55
+ } catch (error) {
56
+ localStorageAccess = false;
57
+ alertNoLocalStorage();
58
+ }
59
+ }
60
+
61
+ function alertNoLocalStorage(wait) {
62
+ if(!localStorageAccess) {
63
+ window.setTimeout(function(){
64
+ let msg = '';
65
+ msg += 'My apologies, your browser settings block the ability to save settings and favorites. If you want those features, you have 3 options:\n';
66
+ msg += '1. Use a different browser than Chrome\n'
67
+ msg += '2. Change Chrome settings\n';
68
+ msg += '3. Download the app to use offline\n\n';
69
+ 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';
70
+ msg += '1. In settings, click "Privacy and security"\n';
71
+ msg += '2. Click "Third-party cookies" and set it to "Allow third-party cookies"\n';
72
+ msg += '3. Unfortunately, that will allow all 3rd-party cookies on all sites, which is exactly what Google wants.\n';
73
+ alert(msg);
74
+ },wait);
75
+ }
76
  }
77
 
78
  function updateTagsConcatenated() {
 
103
  }
104
 
105
  function loadEditedArtists() {
106
+ if(localStorageAccess) {
107
+ const arr = JSON.parse(localStorage.getItem('editedArtists')) || [];
108
+ editedArtists = new Set(arr);
109
+ let proto = window.location.protocol;
110
+ let anyChanges = false;
111
+ for (let i=0, il=artistsData.length; i<il; i++) {
112
+ // find match in artistsData if first and last names match
113
+ let artist = artistsData[i];
114
+ let artistFound = Array.from(editedArtists).find(editedA => editedA[0] === artist[0] && editedA[1] === artist[1]);
115
+ if(artistFound) {
116
+ // check if the edit now matches the original
117
+ let match = true;
118
+ for (let j=0, jl=artist.length; j<jl; j++) {
119
+ if (artist[j] !== artistFound[j]) {
120
+ match = false;
121
+ }
122
  }
123
+ if(match) {
124
+ anyChanges = true;
125
+ editedArtists.delete(artistFound);
126
+ } else {
127
+ if (!proto.startsWith('http')) {
128
+ // if this is a local file, then update artistData with the saved edits
129
+ artistsData[i] = artistFound;
130
+ }
131
  }
132
  }
133
  }
134
+ if(anyChanges) {
135
+ localStorage.setItem('editedArtists', JSON.stringify(Array.from(editedArtists)));
136
+ }
137
  }
138
  }
139
 
 
345
  }
346
 
347
  function loadCheckboxesState() {
348
+ if(localStorageAccess) {
349
+ let state = JSON.parse(localStorage.getItem('tagsChecked')) || {};
350
+ let allChecked = true;
351
+ for (let name in state) {
352
+ if (document.querySelector('input[name="'+name+'"]')) {
353
+ document.querySelector('input[name="'+name+'"]').checked = state[name];
354
+ if(name != 'mode' && name != 'use_categories') {
355
+ if(!state[name]) {
356
+ allChecked = false;
357
+ }
358
  }
359
  }
360
  }
361
+ if(!allChecked) {
362
+ document.querySelector('input[name="check-all"]').checked = false;
363
+ }
364
  }
365
  }
366
 
367
  function storeCheckboxState(checkbox) {
368
+ if(localStorageAccess) {
369
+ let state = JSON.parse(localStorage.getItem('tagsChecked')) || {};
370
+ state[checkbox.name] = checkbox.checked;
371
+ localStorage.setItem('tagsChecked', JSON.stringify(state));
372
+ }
373
  }
374
 
375
  function storeCheckboxStateAll(isChecked) {
376
+ if(localStorageAccess) {
377
+ let state = {};
378
+ var checkboxes = document.querySelectorAll('input[type="checkbox"]');
379
+ checkboxes.forEach(function(checkbox) {
380
+ let isTop = checkbox.parentNode.classList.contains('top_control');
381
+ if(!isTop || checkbox.name == 'favorite') {
382
+ // is a tag checkbox, not a setting
383
+ if(isChecked) {
384
+ state[checkbox.name] = true;
385
+ } else {
386
+ state[checkbox.name] = false;
387
+ }
388
  }
389
+ });
390
+ localStorage.setItem('tagsChecked', JSON.stringify(state));
391
+ }
392
  }
393
 
394
  function loadOptionsState() {
395
+ if(localStorageAccess) {
396
+ let state = JSON.parse(localStorage.getItem('tagsChecked')) || {};
397
+ if(state['prompt']) {
398
+ document.getElementById('options_prompts').querySelectorAll('.selected')[0].classList.remove('selected');
399
+ document.getElementById(state['prompt']).classList.add('selected');
400
+ if(state['prompt'] == 'promptA') {
401
+ imgTypeShown = 0;
402
+ } else if(state['prompt'] == 'promptP') {
403
+ imgTypeShown = 1;
404
+ } else if(state['prompt'] == 'promptL') {
405
+ imgTypeShown = 2;
406
+ }
407
+ } else {
408
+ // promptA is already highlighted by HTML
409
  imgTypeShown = 0;
 
 
 
 
410
  }
411
+ if(state['artistSort']) {
412
+ document.getElementById('options_artist_sort').querySelectorAll('.selected')[0].classList.remove('selected');
413
+ document.getElementById(state['artistSort']).classList.add('selected');
414
+ } else {
415
+ // sortAR is already highlighted by HTML
416
+ }
417
+ if(state['tagSort']) {
418
+ document.getElementById('options_tag_sort').querySelectorAll('.selected')[0].classList.remove('selected');
419
+ document.getElementById(state['tagSort']).classList.add('selected');
420
+ } else {
421
+ // sortTC is already highlighted by HTML
422
+ }
 
 
 
423
  }
424
  }
425
 
 
466
  }
467
 
468
  function storeOptionsState() {
469
+ if(localStorageAccess) {
470
+ let state = JSON.parse(localStorage.getItem('tagsChecked')) || {};
471
+ if(document.getElementById('promptA').classList.contains('selected')) {
472
+ state['prompt'] = 'promptA';
473
+ } else if(document.getElementById('promptP').classList.contains('selected')) {
474
+ state['prompt'] = 'promptP';
475
+ } else {
476
+ state['prompt'] = 'promptL';
477
+ }
478
+ if(document.getElementById('sortAR').classList.contains('selected')) {
479
+ state['artistSort'] = 'sortAR';
480
+ } else {
481
+ state['artistSort'] = 'sortAA';
482
+ }
483
+ if(document.getElementById('sortTC').classList.contains('selected')) {
484
+ state['tagSort'] = 'sortTC';
485
+ } else {
486
+ state['tagSort'] = 'sortTA';
487
+ }
488
+ localStorage.setItem('tagsChecked', JSON.stringify(state));
489
  }
 
490
  }
491
 
492
  function rotatePromptsImages() {
 
821
  document.getElementById('export').classList.add('shown');
822
  // favorites
823
  var textareaF = document.getElementById('export_favorites_list');
824
+ var favoritedArtists = false;
825
+ if(localStorageAccess) {
826
+ var favorites = localStorage.getItem('favoritedArtists');
827
+ var value = '';
828
+ if(favorites) {
829
+ value += 'You have favorited these artists:\r\n';
830
+ for (let key in JSON.parse(favorites)) {
831
+ if (JSON.parse(favorites)[key] === true) {
832
+ let names = key.split("|");
833
+ if(!names[0]) { names[0] = '(no first name)'; }
834
+ value += '•' + names[0] + ',' + names[1] + '\r\n';
835
+ }
836
  }
837
+ 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' + favorites;
838
+ textareaF.value = value;
839
+ } else {
840
+ value += 'You haven\'t favorited any artists yet.\r\n\r\n';
841
+ value += 'To import favorites that you exported earlier, paste the text into this text box, and click "import".';
842
+ textareaF.value = value;
843
  }
 
 
 
 
 
844
  }
845
  // edits
846
  var textareaE = document.getElementById('export_edits_list');
 
884
  }
885
 
886
  function importFavorites() {
887
+ if(localStorageAccess) {
888
+ let el = document.getElementById('export_favorites_list');
889
+ let favorites = el.value;
890
+ let startCount = (favorites.match(/{/g) || []).length;
891
+ let endCount = (favorites.match(/}/g) || []).length;
892
+ if (startCount > 1 || endCount > 1) {
893
+ el.value = 'That text can\'t be imported because it contains multiple curly brackets {}.'
894
+ return null;
895
+ }
896
+ let start = favorites.indexOf('{');
897
+ let end = favorites.lastIndexOf('}');
898
+ if (start === -1 || end === -1) {
899
+ el.value = 'That text can\'t be imported because it contains zero curly brackets {}.'
900
+ return null;
901
+ }
902
+ let jsonString = favorites.substring(start, end + 1);
903
+ try {
904
+ let jsonObject = JSON.parse(jsonString);
905
+ // Check structure of each key-value pair in jsonObject
906
+ for (let key in jsonObject) {
907
+ let value = jsonObject[key];
908
+ if (!key.includes('|') || typeof value !== 'boolean') {
909
+ el.value = 'That text can\'t be imported because the JSON string it contains doesn\'t contain a valid list of artists.'
910
+ return null;
911
+ }
912
+ }
913
+ if(confirm('This will overwrite any saved favorites. Are you sure?')) {
914
+ localStorage.setItem('favoritedArtists', jsonString);
915
+ alert('Favorites were imported!');
916
+ loadFavoritesState();
917
+ } else {
918
+ alert('Okay, you have cancelled the import.');
919
  return null;
920
  }
921
+ } catch(e) {
922
+ el.value = 'That text can\'t be imported because it doesn\'t contain a valid JSON sting.'
 
 
 
 
 
923
  return null;
924
  }
 
 
 
925
  }
926
  }
927
 
 
1098
  }
1099
 
1100
  function loadMostUsedTags() {
1101
+ if(localStorageAccess) {
1102
+ let state = JSON.parse(localStorage.getItem('mustUsedTags')) || {};
1103
+ let mostUsedCategory = document.querySelector('[data-category-name="important"]');
1104
+ for(let tag in state) {
1105
+ if (state[tag]) {
1106
+ let label = document.querySelector('input[name="'+ tag +'"]');
1107
+ if(label) {
1108
+ label = label.parentNode;
1109
+ label.classList.add('is_most_used');
1110
+ label.querySelectorAll('.most_used_indicator')[0].textContent = '-';
1111
+ mostUsedCategory.after(label);
1112
+ updateTagArrayToMatchMostUsed(true,label,tag);
1113
+ }
1114
  }
1115
+ };
1116
+ }
1117
  }
1118
 
1119
  function updateTagArrayToMatchMostUsed(isAdding,label,tag) {
 
1145
  }
1146
 
1147
  function storeMostUsedState(label) {
1148
+ if(localStorageAccess) {
1149
+ var name = label.querySelector('input').name;
1150
+ let state = JSON.parse(localStorage.getItem('mustUsedTags')) || {};
1151
+ state[name] = label.classList.contains('is_most_used');
1152
+ localStorage.setItem('mustUsedTags', JSON.stringify(state));
1153
+ }
1154
  }
1155
 
1156
  function enterExitEditMostUsedMode(doExit) {
1157
+ if(localStorageAccess) {
1158
+ let inputs = Array.from(document.querySelectorAll('input'));
1159
+ if(editMostUsedMode || doExit) {
1160
+ // exit edit mode
1161
+ editMostUsedMode = false;
1162
+ document.getElementById('edit_most_used').textContent = 'edit';
1163
+ document.getElementById('layout').classList.remove('edit_mode');
1164
+ inputs.forEach(function(input) {
1165
+ input.disabled = false;
1166
+ });
1167
+ let labels = Array.from(document.querySelectorAll('.was_moved'));
1168
+ labels.forEach(function(label) {
1169
+ // clean up classes added to track moved tags during edit mode
1170
+ label.classList.remove('was_moved');
1171
+ })
1172
+ document.getElementById('toggles').style.width = 'calc(' + gutterEndPercentX + '% - 20px)';
1173
+ document.getElementById('gutter').style.left = gutterEndPercentX + '%';
1174
+ document.getElementById('image-container').style.marginLeft = 'calc(' + gutterEndPercentX + '% + 50px)';
1175
+ updateArtistsCountPerCategory();
1176
+ } else {
1177
+ // enter edit mode
1178
+ editMostUsedMode = true;
1179
+ document.getElementById('edit_most_used').textContent = 'exit editing';
1180
+ document.getElementById('layout').classList.add('edit_mode');
1181
+ inputs.forEach(function(input) {
1182
+ input.disabled = true;
1183
+ });
1184
+ document.getElementById('toggles').style.width = '';
1185
+ document.getElementById('gutter').style.left = '';
1186
+ document.getElementById('image-container').style.marginLeft = '';
1187
+ }
1188
  } else {
1189
+ alertNoLocalStorage(0);
 
 
 
 
 
 
 
 
 
1190
  }
1191
  }
1192
 
 
1263
  }
1264
 
1265
  function loadFavoritesState() {
1266
+ if(localStorageAccess) {
1267
+ let state = JSON.parse(localStorage.getItem('favoritedArtists')) || {};
1268
+ let artists = document.getElementsByClassName('image-item');
1269
+ for(let artist of artists) {
1270
+ let artistName = artist.getElementsByClassName('firstN')[0].textContent + '|' + artist.getElementsByClassName('lastN')[0].textContent;
1271
+ if(state[artistName]) {
1272
+ artist.classList.add('favorite');
1273
+ } else {
1274
+ artist.classList.remove('favorite');
1275
+ }
1276
  }
1277
+ updateFavoritesCount();
1278
  }
 
1279
  }
1280
 
1281
  function storeFavoriteState(artist) {
1282
+ if(localStorageAccess) {
1283
+ var artistName = artist.getElementsByClassName('firstN')[0].textContent + '|' + artist.getElementsByClassName('lastN')[0].textContent;
1284
+ var isFavorited = artist.classList.contains('favorite');
1285
+ let state = JSON.parse(localStorage.getItem('favoritedArtists')) || {};
1286
+ state[artistName] = isFavorited;
1287
+ localStorage.setItem('favoritedArtists', JSON.stringify(state));
1288
+ } else {
1289
+ alertNoLocalStorage(0);
1290
+ }
1291
  }
1292
 
1293
  function updateFavoritesCount() {
 
1546
  }
1547
 
1548
  function editTagsClicked(clickedImageItem) {
1549
+ if(localStorageAccess) {
1550
+ let indicatorEl = clickedImageItem.querySelector('.art_edit span');
1551
+ if(indicatorEl.textContent == '✍️') {
1552
+ let artistWasInEditMode = editTagsFindArtistInEditMode(clickedImageItem);
1553
+ if(!artistWasInEditMode) {
1554
+ doAlert('Read help ⁉️ first',1);
1555
+ }
1556
+ editTagsEnterEditMode(clickedImageItem);
1557
+ } else {
1558
+ editTagsFindArtistInEditMode();
1559
  }
 
1560
  } else {
1561
+ alertNoLocalStorage(0);
1562
  }
1563
  }
1564
 
 
1747
  }
1748
 
1749
  function saveTagsForArtist(tagArea) {
1750
+ if(localStorageAccess) {
1751
+ // get new tags
1752
+ let tagLabels = tagArea.querySelectorAll('label');
1753
+ let newTagsArr = [];
1754
+ let artistKnown = true;
1755
+ tagLabels.forEach(function(label) {
1756
+ let input = label.querySelector('input');
1757
+ if(input.value == 'known') {
1758
+ artistKnown = input.checked;
1759
+ } else {
1760
+ if(input.checked) {
1761
+ newTagsArr.push(input.value);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1762
  }
1763
  }
1764
+ });
1765
+ // find match in artistsData
1766
+ let firstN = tagArea.closest('.image-item').querySelector('.firstN').textContent;
1767
+ let lastN = tagArea.closest('.image-item').querySelector('.lastN').textContent;
1768
+ let edit = [];
1769
+ for (let i=0, il=artistsData.length; i<il; i++) {
1770
+ let artist = artistsData[i];
1771
+ if(artist[0] == lastN && artist[1] == firstN) {
1772
+ // artists can have a tag in the format of "added-YYYY-MM-DD"
1773
+ // this was stripped earlier, so we need to add it back in
1774
+ let oldTagsArr = artist[2].split('|');
1775
+ for (let j=oldTagsArr.length-1; j>=0; j--) {
1776
+ // loop backwards because it should be at the end
1777
+ if(oldTagsArr[j].match(/added-(\d|-)*/)) {
1778
+ newTagsArr.push(oldTagsArr[j]);
1779
+ }
1780
+ }
1781
+ let newTagsStr = newTagsArr.join('|');
1782
+ artist[2] = newTagsStr;
1783
+ // in db, true = hide unknown, but here true = known
1784
+ if(artistKnown) {
1785
+ artist[3] = false;
1786
+ } else {
1787
+ artist[3] = true;
1788
+ }
1789
+ edit = artist;
1790
+ break;
1791
  }
 
 
1792
  }
1793
+ // replace old edits with new edits
1794
+ for (let i=0, il=editedArtists.length; i<il; i++) {
1795
+ let oldEdit = editedArtists[i];
1796
+ if(edit[0] == oldEdit[0] && edit[1] == oldEdit[1]) {
1797
+ editedArtists.delete(oldEdit);
1798
+ }
1799
  }
1800
+ editedArtists.add(edit)
1801
+ // save edited artists locally
1802
+ localStorage.setItem('editedArtists', JSON.stringify(Array.from(editedArtists)));
1803
  }
 
 
 
1804
  }
1805
 
1806
  function deleteAllEdits() {
1807
+ if(localStorageAccess) {
1808
+ if(confirm('This will delete all of your edits. Are you sure?')) {
1809
+ localStorage.removeItem('editedArtists');
1810
+ alert('official database restored! this page will reload...');
1811
+ location.reload();
1812
+ } else {
1813
+ alert('restore was cancelled!');
1814
+ }
1815
  }
1816
  }
1817
  //