diff options
Diffstat (limited to 'static/script.js')
-rw-r--r-- | static/script.js | 272 |
1 files changed, 272 insertions, 0 deletions
diff --git a/static/script.js b/static/script.js new file mode 100644 index 0000000..4dfd3c6 --- /dev/null +++ b/static/script.js @@ -0,0 +1,272 @@ +"use strict"; + +var questionState = new Map(); +var globalLevel; +var globalId; + +function randomId() { + return works[randomInt(0, works.length-1)]; +} + +var queryTypes = [ + { + modes: new Set(["novice", "intermediate", "expert"]), + type: "hue", + inputType: "range", + generateInputAttrs: function(attrs) { + return new Map([["min", "0"], ["max", "359"], ["value", "180"]]); + }, + changeEvents: ["input"], + calculateScore: function(attrs) { + var hue = attrs.get("hue"); + return Math.min(360 - hue, hue); + }, + imgAttrsModifier: function(evt) { + var imgAttrs = questionState.get(globalId); + var hueRandomness = imgAttrs.get("hueRandomness"); + var newHue = Number(evt.target.value); + imgAttrs.set("hue", mod(newHue + (hueRandomness - 180), 360)); + redraw(); + }, + filterStyleString: function(attrs) { + return 'hue-rotate(' + attrs.get("hue") + 'deg)' + }, + generateDefaultAttrs: function() { + var r = randomInt(0, 359); + return new Map([["hueRandomness", r], ["hue", r]]); + }, + generateInputId: function(id) { + return `hue-input-${id}`; + } + }, + { + modes: new Set(["intermediate", "expert"]), + type: "contrast", + inputType: "range", + generateInputAttrs: function(attrs) { + return new Map([["min", attrs.get("contrastLowerBound")], ["max", attrs.get("contrastUpperBound")], ["value", attrs.get("contrast")]]); + }, + changeEvents: ["input"], + calculateScore: function(attrs) { + var contrast = attrs.get("contrast"); + return abs(100 - contrast); + }, + imgAttrsModifier: function(evt) { + var imgAttrs = questionState.get(globalId); + imgAttrs.set("contrast", Number(evt.target.value)); + redraw(); + }, + filterStyleString: function(attrs) { + return 'contrast(' + attrs.get("contrast") + '%)' + }, + generateDefaultAttrs: function() { + var r = randomInt(50, 100); + var mid = r + 50; + var lowerBound = r; + var upperBound = r + 100; + return new Map([["contrast", mid], ["contrastLowerBound", lowerBound], ["contrastUpperBound", upperBound]]); + }, + generateInputId: function(id) { + return `contrast-input-${id}`; + } + }, + { + modes: new Set(["expert"]), + type: "saturation", + inputType: "range", + generateInputAttrs: function(attrs) { + return new Map([["min", attrs.get("saturationLowerBound")], ["max", attrs.get("saturationUpperBound")], ["value", attrs.get("saturation")]]); + }, + changeEvents: ["input"], + calculateScore: function(attrs) { + var saturation = attrs.get("saturation"); + return abs(100 - saturation); + }, + imgAttrsModifier: function(evt) { + var imgAttrs = questionState.get(globalId); + imgAttrs.set("saturation", Number(evt.target.value)); + redraw(); + }, + filterStyleString: function(attrs) { + return 'saturate(' + attrs.get("saturation") + '%)' + }, + generateDefaultAttrs: function() { + var r = randomInt(50, 100); + var mid = r + 50; + var lowerBound = r; + var upperBound = r + 100; + return new Map([["saturation", mid], ["saturationLowerBound", lowerBound], ["saturationUpperBound", upperBound]]); + }, + generateInputId: function(id) { + return `saturation-input-${id}`; + } + } +]; + +function load() { + var urlParams = new URLSearchParams(window.location.search); + var id = urlParams.get('id') + var level = urlParams.get('level') + + if (!id) { + id = randomId(); + level = "novice"; + } + + startGame(id, level); +} + +function renderArtist(s) { + return s.split('-').map(capitalize).join(' '); +} + +function getArtist(id) { + var info = artists[id] + var artist = manifest.features[1].type.names[info] + return renderArtist(artist); +} + +function startGame(id, level) { + document.getElementById('answer').innerHTML = ''; + document.getElementById('scoring').innerHTML = ''; + document.getElementById('sliders').innerHTML = ''; + document.getElementById('question').innerHTML = ''; + globalId = id; + globalLevel = level; + addQuestion(id, level); + finalizeQuestion(id, level); + redraw(); + document.getElementById('copy').addEventListener('click', function() { + navigator.clipboard.writeText(`${location.origin}/countervisual?id=${id}&level=${level}`) + }); +} + +function redraw() { + var el = document.getElementById('img'); + var styleString = '' + for (var queryType of queryTypes) { + if (queryType.modes.has(globalLevel)) { + var attrs = questionState.get(globalId); + styleString += ` ${queryType.filterStyleString(attrs)} ` + } + } + el.setAttribute("style", 'filter: ' + styleString); +} + +function addQuestion(id, level) { + var attrs = new Map(); + for (var queryType of queryTypes) { + if (queryType.modes.has(level)) { + attrs = new Map([...attrs, ...queryType.generateDefaultAttrs()]); + } + } + + document.getElementById('question').innerHTML = `<figcaption>Countervisual #${id}</figcaption>` + generateImgDom(id, "img"); + document.getElementById('sliders').innerHTML = generateAttrDOM(id, level, attrs) + questionState.set(id, attrs); +} + +function getImgUrl(id) { + return `/countervisual/static/data/${id}.jpg` +} + +function generateImgDom(id, htmlId) { + return `<img id="${htmlId}" src="${getImgUrl(id)}"></img>` +} + +function finalizeQuestion(id, level) { + for (var queryType of queryTypes) { + var inputId = queryType.generateInputId(id); + var input = document.getElementById(inputId); + if (queryType.modes.has(level)) { + for (var changeEvent of queryType.changeEvents) { + input.addEventListener(changeEvent, queryType.imgAttrsModifier); + } + } + } +} + +function generateAttrDOM(id, level, attrs) { + var dom = ``; + for (var queryType of queryTypes) { + if (queryType.modes.has(level)) { + var attrString = generateAttrString(queryType.generateInputAttrs(attrs)); + dom += `<label>${queryType.type}</label><br> + <input id="${queryType.generateInputId(id)}" style="width: 45%" type="${queryType.inputType}" ${attrString}> + <br>`; + } + } + dom += `</div>`; + return dom; +} + +document.onload = load(); + +document.getElementById('score-game').addEventListener('click', function(evt) { + var s = score(); + var imageUrl = location.protocol + '//' + location.host + location.pathname + getImgUrl(globalId) + var lensUrl = `https://www.google.com/searchbyimage?image_url=${imageUrl}&client=app` + document.getElementById('scoring').innerHTML = `<p>You were ${s} points away from zero.</p> + <p>This work is by <a target="_blank" href="${lensUrl}">${getArtist(globalId)}</a>.</p>` + document.getElementById('answer').innerHTML = `<figcaption>Solution</figcaption>` + generateImgDom(globalId, "ans"); + evt.preventDefault(); +}) + +document.getElementById('reset').addEventListener('click', function(evt) { + startGame(globalId, globalLevel); +}); + +document.getElementById('random-novice').addEventListener('click', function(evt) { + startGame(randomId(), "novice"); +}); + +document.getElementById('random-intermediate').addEventListener('click', function(evt) { + startGame(randomId(), "intermediate"); +}); + +document.getElementById('random-expert').addEventListener('click', function(evt) { + startGame(randomId(), "expert"); +}); + +function generateAttrString(attrs) { + var attrString = ""; + for (var [attr, val] of attrs) { + attrString += ` ${attr}=${val}` + } + return attrString; +} + +// Source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random +function randomInt(min, max) { + var min = Math.ceil(min); + var max = Math.floor(max); + return Math.floor(Math.random() * (max - min + 1)) + min; +} + +function score() { + var total = 0; + for (var queryType of queryTypes) { + if (queryType.modes.has(globalLevel)) { + total += queryType.calculateScore(questionState.get(globalId)); + } + } + return total; +} + +function mod(p, n) { + var z = (p % n); + if (z >= 0) { + return z; + } else { + return z + n; + } +} + +function abs(a) { + return Math.max(a, -a); +} + +function capitalize(s) { + return s.substr(0,1).toUpperCase() + s.substr(1); +} + |