-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
297 additions
and
70 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
const apiURL = 'https://api.artic.edu/api/v1/search'; | ||
const noDepartmentTerm = 'None (No Department Association)'; | ||
|
||
function getJson(body, callback, forceNew) { | ||
let request = new XMLHttpRequest(); | ||
request.open('POST', apiURL, true); | ||
request.setRequestHeader('Content-Type', 'application/json'); | ||
request.onreadystatechange = function () { | ||
if (this.readyState === 4 && this.status === 200) { | ||
callback(JSON.parse(this.responseText), forceNew); | ||
} | ||
}; | ||
request.send(JSON.stringify(body)); | ||
} | ||
|
||
function getJsonData(body) { | ||
return new Promise((resolve, reject) => { | ||
setTimeout(() => { | ||
reject('request timed out'); | ||
}, 30 * 1000); | ||
getJson(body, (data) => { | ||
resolve(data); | ||
}); | ||
}); | ||
} | ||
|
||
function escape(s) { | ||
const bad1 = /&/g; | ||
const good1 = '&'; | ||
|
||
const bad2 = /</g; | ||
const good2 = '<'; | ||
|
||
const bad3 = />/g; | ||
const good3 = '>'; | ||
|
||
const bad4 = /"/g; | ||
const good4 = '"'; | ||
|
||
const bad5 = /'/g; | ||
const good5 = '''; | ||
|
||
// prettier-ignore | ||
return s | ||
.replace(bad1, good1) | ||
.replace(bad2, good2) | ||
.replace(bad3, good3) | ||
.replace(bad4, good4) | ||
.replace(bad5, good5); | ||
} | ||
|
||
function merge(target, source) { | ||
for (const key in source) { | ||
if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) { | ||
if (!target[key] || typeof target[key] !== 'object') { | ||
target[key] = {}; | ||
} | ||
merge(target[key], source[key]); | ||
} else { | ||
target[key] = source[key]; | ||
} | ||
} | ||
} | ||
|
||
// initialize settings with defaults | ||
const settings = { | ||
dailyMode: false, | ||
departmentOptions: { | ||
options: [], | ||
lastFetched: null, | ||
selected: [], | ||
}, | ||
}; | ||
|
||
// load settings if they exist | ||
const extensionSettingsKey = 'extensionSettings'; | ||
merge(settings, JSON.parse(localStorage.getItem(extensionSettingsKey))); | ||
saveSettings(); // save in case there are new default settings or settings are not persisted yet | ||
|
||
function getSettings() { | ||
return settings; | ||
} | ||
|
||
function getStoredSettings() { | ||
// ensure we are always reading from localStorage so we don't load stale data | ||
return JSON.parse(localStorage.getItem(extensionSettingsKey)); | ||
} | ||
|
||
function saveSettings() { | ||
localStorage.setItem(extensionSettingsKey, JSON.stringify(settings)); | ||
} | ||
|
||
const filterFields = { | ||
department: 'department_title.keyword', | ||
}; | ||
|
||
const artworkCacheKeys = { | ||
// LocalStorage keys for reference | ||
savedResponseKey: 'response', | ||
preloadedImagesKey: 'preloaded', | ||
preloadingImagesKey: 'preloading', | ||
lastLoadedDateKey: 'lastLoadedDate', | ||
}; | ||
|
||
// prettier-ignore | ||
export { | ||
artworkCacheKeys, | ||
escape, | ||
filterFields, | ||
getJson, | ||
getJsonData, | ||
getSettings, | ||
getStoredSettings, | ||
merge, | ||
noDepartmentTerm, | ||
saveSettings, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
body { | ||
background: #21212c; | ||
color: lightgrey; | ||
font-family: sans-serif; | ||
font-size: 16px; | ||
padding: 0 32px; | ||
} | ||
|
||
label, | ||
legend, | ||
select | ||
{ | ||
font-size: 24px; | ||
} | ||
|
||
select { | ||
margin: 0 24px; | ||
} | ||
|
||
.setting { | ||
margin: 24px 0; | ||
padding: 24px 12px; | ||
} | ||
|
||
.checkbox { | ||
display: block; | ||
margin: 12px; | ||
|
||
input { | ||
margin-right: 12px; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,39 +1,22 @@ | ||
<!DOCTYPE html> | ||
<html> | ||
<html lang="en"> | ||
<head> | ||
<title>Art Institute of Chicago: Art Tab - Options</title> | ||
<style> | ||
body { | ||
background: #21212c; | ||
color: lightgrey; | ||
font-family: sans-serif; | ||
font-size: 16px; | ||
padding: 0 32px; | ||
} | ||
label, select { | ||
font-size: 24px; | ||
} | ||
select { | ||
margin: 0 24px; | ||
} | ||
.setting { | ||
margin: 24px 0; | ||
} | ||
</style> | ||
<link rel="stylesheet" type="text/css" href="options.css" /> | ||
</head> | ||
<body> | ||
<h1>Art Institute of Chicago: Art Tab - Options</h1> | ||
<hr> | ||
<div class="setting"> | ||
<label for="daily">Daily mode:</label> | ||
<fieldset class="setting"> | ||
<legend>Daily mode</legend> | ||
<select id="daily"> | ||
<option value="false" selected>No</option> | ||
<option value="true">Yes</option> | ||
</select> | ||
<span>Reload artwork image once per day instead of on every new tab</span> | ||
</div> | ||
<hr> | ||
|
||
<script src="options.js"></script> | ||
<option value="false" selected>No</option> | ||
<option value="true">Yes</option> | ||
</select> | ||
<span>Reload artwork image once per day instead of on every new tab</span> | ||
</fieldset> | ||
<fieldset class="setting" id="departments"> | ||
<legend>Department Filter</legend> | ||
</fieldset> | ||
<script src="options.js" type="module"></script> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,89 @@ | ||
const extensionSettingsKey = 'extensionSettings'; | ||
const settings = JSON.parse(localStorage.getItem(extensionSettingsKey)) || {}; | ||
// prettier-ignore | ||
import { | ||
artworkCacheKeys, | ||
escape, | ||
filterFields, | ||
getJsonData, | ||
getSettings, | ||
noDepartmentTerm, | ||
saveSettings, | ||
} from './lib.js'; | ||
|
||
const contemporaryArt = 'Contemporary Art'; | ||
|
||
const baseQuery = { | ||
resources: 'artworks', | ||
size: 0, | ||
aggregations: {}, | ||
}; | ||
|
||
const departmentQuery = Object.assign({}, baseQuery); | ||
departmentQuery.aggregations = { | ||
departments: { | ||
terms: { | ||
field: filterFields.department, | ||
}, | ||
}, | ||
}; | ||
|
||
const settings = getSettings(); | ||
|
||
const selectDaily = document.querySelector('#daily'); | ||
selectDaily.value = settings.dailyMode; | ||
|
||
selectDaily.addEventListener('change', e => { | ||
selectDaily.addEventListener('change', (e) => { | ||
settings.dailyMode = e.target.value === 'true'; | ||
localStorage.setItem(extensionSettingsKey, JSON.stringify(settings)); | ||
}) | ||
save(); | ||
}); | ||
|
||
const oneWeekMs = 7 * 24 * 60 * 60 * 1000; | ||
const lastFetchedMoreThanAWeekAgo = (Date.now() - settings.departmentOptions.lastFetched) > oneWeekMs; | ||
|
||
if (settings.departmentOptions.options.length === 0 || lastFetchedMoreThanAWeekAgo) { | ||
const departmentData = await getJsonData(departmentQuery); | ||
// prettier-ignore | ||
const departmentOptions = departmentData.aggregations.departments.buckets | ||
.map((b) => b.key) | ||
.filter(o => o !== contemporaryArt) // for some reason, filtering on contemporary art yields zero results, despite there being many artworks with that department | ||
.sort(); | ||
departmentOptions.push(noDepartmentTerm); | ||
settings.departmentOptions.options = departmentOptions; | ||
settings.departmentOptions.lastFetched = Date.now(); | ||
save(); | ||
} | ||
|
||
const divDepartments = document.getElementById('departments'); | ||
|
||
for (const o of settings.departmentOptions.options) { | ||
const sanitized = escape(o); | ||
const template = document.createElement('div'); | ||
template.innerHTML = `<label class="checkbox"><input type="checkbox" value="${sanitized}">${sanitized}</label>`; | ||
divDepartments.append(...template.children); | ||
} | ||
|
||
function updateDepartment(e) { | ||
settings.departmentOptions.selected = Array.from(divDepartments.querySelectorAll('input:checked')).map( | ||
(i) => i.value | ||
); | ||
save(); | ||
// clear cached artwork data so that preferences are respected immediately | ||
Object.values(artworkCacheKeys).forEach(k => localStorage.removeItem(k)); | ||
} | ||
|
||
if (settings.departmentOptions.selected.length === 0) { | ||
divDepartments.querySelectorAll('input').forEach((i) => (i.checked = true)); | ||
} else { | ||
settings.departmentOptions.selected.forEach((o) => { | ||
const option = divDepartments.querySelector(`[value="${o}"]`); | ||
// guard against options disappearing or being renamed | ||
if(option) { | ||
option.checked = true; | ||
} | ||
}); | ||
} | ||
|
||
divDepartments.querySelectorAll('input').forEach((i) => i.addEventListener('change', updateDepartment)); | ||
|
||
function save() { | ||
saveSettings(); | ||
} |
Oops, something went wrong.