+++ /dev/null
-// (() => {
-"use strict";
-const methods = [ "loc", "date", "sub" ];
-let up_dom, photo_dom;
-// pointers to <ul>
-let meth_dom, cat_dom, thumb_dom;
-// pointers to [<li>]
-let meth_list = new Object();
-// pointers to photos
-let photos;
-let state = [];
-
-function loadJson(text) {
- let json = JSON.parse(text);
- photos = json.photos;
- photos.forEach(p => {
- let t = document.createElement("img");
- t.alt = p.desc;
- t.src = `/pictures/${p.name}_thm.jpg`;
-
- p.thm = document.createElement("div");
- p.thm.classList.add("thumb-tile");
- p.thm.appendChild(t);
- p.thm.addEventListener("click", () => {
- event.stopPropagation();
- loadPhoto(p);
- });
-
- p.img = document.createElement("img");
- p.img.alt = p.desc;
- p.img.classList.add("responsive");
- p.img.src = `/pictures/${p.name}_sm.jpg`;
- });
-
- methods.forEach(m => {
- let a = [];
- json[m].forEach(
- s => {
- let e = document.createElement("li");
- e.innerText = s;
- e.id = s;
- e.addEventListener("click", () => {
- event.stopPropagation();
- loadCategory(m, e);
- });
- a.push(e);
- });
- meth_list[m] = a;
- });
-}
-
-function keyDown(e) {
- if (e.keyCode === 27 || e.keyCode === 32) {
- console.log("close picture");
- photo_dom.classList.add("hidden");
- }
-}
-
-function loadPhoto(p) {
- removeAll(photo_dom);
- console.log("load picture " + p.name);
- photo_dom.appendChild(p.img);
- photo_dom.classList.remove("hidden");
-}
-
-function loadCategory(m, node) {
- let s = node.innerText;
- console.log(`load category ${s}:${m}`);
- let a = siblings(node);
-
- removeAll(thumb_dom);
- if (peek(state) === s) {
- ascend(node);
- } else {
- state.push(s);
- photos.filter(p => p[m] === s)
- .forEach(p => thumb_dom.appendChild(p.thm));
-
- a.forEach(e => {
- e.classList.add("deselected")
- e.classList.remove("selected");
- });
- node.classList.remove("deselected");
- node.classList.add("selected");
- }
-}
-
-function loadMethod(node) {
- let m = node.id;
- console.log(`load method ${m}`);
- let a = siblings(node);
-
- removeAll(cat_dom);
- if (peek(state) === m) {
- ascend(node);
- } else {
- state.push(m);
- meth_list[m].forEach(e => cat_dom.appendChild(e));
-
- node.appendChild(cat_dom);
- a.forEach(e => {
- e.classList.add("deselected")
- e.classList.remove("selected");
- });
- node.classList.remove("deselected");
- node.classList.add("selected");
- }
-}
-
-function ascend(node) {
- state.pop();
- removeLast(node);
- siblings(node).forEach(e => {
- e.classList.remove("deselected")
- e.classList.remove("selected");
- });
-}
-
-function removeAll(e) {
- while(e.lastChild) {
- e.removeChild(e.lastChild);
- }
-}
-function siblings(n) { return Array.from(n.parentNode.children); }
-function removeLast(e) { e.removeChild(e.lastChild); }
-function peek(a) { return a[a.length-1]; }
-
-document.addEventListener("DOMContentLoaded",
- (event) => {
- let xhttp = new XMLHttpRequest();
- xhttp.onload = (() => loadJson(xhttp.responseText));
- xhttp.open("GET", "pictures.json");
- xhttp.send();
-
- cat_dom = document.createElement("ul");
-
- thumb_dom = document.getElementById("thumbs");
- meth_dom = document.getElementById("methods");
- up_dom = document.getElementById("up");
- photo_dom = document.getElementById("photo");
-
- photo_dom.addEventListener("click", () => {
- photo_dom.classList.add("hidden");
- });
-
- Array.from(meth_dom.children)
- .forEach(e => {
- e.addEventListener("click", () => {
- event.stopPropagation();
- loadMethod(e);
- });
- });
-
- document.addEventListener("keydown", keyDown);
- });
-// })();
<head>
<meta charset="UTF-8">
<title>MadKous Photography</title>
- <link rel="stylesheet" type="text/css" href="style.css"></script>
- <script type="text/javascript" src="driver.js"></script>
+ <link rel="stylesheet" type="text/css" href="styles/main.css"></script>
+ <!-- util must be first; driver must be last -->
+ <script type="text/javascript" src="scripts/util.js"></script>
+ <script type="text/javascript" src="scripts/photo.js"></script>
+ <script type="text/javascript" src="scripts/click.js"></script>
+ <script type="text/javascript" src="scripts/dom.js"></script>
+ <script type="text/javascript" src="scripts/main.js"></script>
</head>
<body class="parent">
+ <div id="logo">
+ <img src="logo_sm.png" alt="MadKous">
+ </div>
<div class="grid parent">
<div class="menu">
- <ul id="methods">
- Filter
- <li id="loc">by Location</li>
+ <ul id="methods">Filter<li id="loc">by Location</li>
<li id="sub">by Subject</li>
<li id="date">by Date</li>
</ul>
--- /dev/null
+var CLICK = (click => {
+ click.category = (m, node) => {
+ let s = node.innerText;
+
+ if (UTIL.is_selected(node)) {
+ if(PHOTO.removeFilter(m, s)) {
+ UTIL.siblings(node).forEach(e => {
+ UTIL.un_deselected(e);
+ UTIL.normal(e);
+ });
+ }
+ UTIL.un_selected(node);
+ } else if (UTIL.is_normal(node)) {
+ PHOTO.addFilter(m, s, p => p[m] === s);
+ UTIL.siblings(node).forEach(e => {
+ UTIL.deselected(e);
+ UTIL.un_normal(e);
+ });
+ UTIL.un_deselected(node);
+ UTIL.selected(node);
+ } else { // assumed to be 'deselected'
+ PHOTO.addFilter(m, s, p => p[m] === s);
+ UTIL.un_deselected(node);
+ UTIL.selected(node);
+ }
+ }
+
+ click.method = (node) => {
+ let m = node.id;
+ console.log(`click method ${m}`);
+
+ if (node.classList.contains("selected")) {
+ Array.from(node.children).forEach(UTIL.hidden);
+ click.close(node);
+ } else {
+ Array.from(node.children).forEach(UTIL.un_hidden);
+ click.open(node);
+ }
+ }
+
+ click.open = (node) => {
+ UTIL.siblings(node).forEach(e => {
+ UTIL.deselected(e);
+ UTIL.un_selected(e);
+ UTIL.un_normal(e);
+ });
+ UTIL.un_deselected(node);
+ UTIL.un_normal(node);
+ UTIL.selected(node);
+ }
+
+ click.close = (node) => {
+ UTIL.siblings(node).forEach(e => {
+ UTIL.un_deselected(e);
+ UTIL.un_selected(e);
+ UTIL.normal(e);
+ });
+ }
+
+ return click;
+})(CLICK || {});
--- /dev/null
+var DOM = (dom => {
+ // main photo container
+ dom.photo_dom;
+ // pointers to <ul>
+ dom.meth_dom;
+ // thumbnail container
+ dom.thumb_dom;
+ // lists of <li> by method
+ dom.meth_list = new Object();
+
+ //// dom creation ////
+ dom.makeThumbnail = (p) => {
+ let t = document.createElement("img");
+ let e = document.createElement("div");
+
+ t.alt = p.desc;
+ t.src = `pictures/${p.name}_thm.jpg`;
+
+ e.appendChild(t);
+ e.addEventListener("click", () => {
+ event.stopPropagation();
+ dom.loadPhoto(p);
+ });
+ return e;
+ }
+
+ dom.makePicture = (p) => {
+ let e = document.createElement("img");
+ e.alt = p.desc;
+ e.src = `pictures/${p.name}_sm.jpg`;
+ return e;
+ }
+
+ dom.makeCategory = (s, m) => {
+ let e = document.createElement("li");
+ e.innerText = s;
+ e.id = s;
+ e.addEventListener("click", () => {
+ event.stopPropagation();
+ CLICK.category(m, e);
+ });
+ UTIL.normal(e);
+ return e;
+ }
+
+ dom.makeList = (l) => {
+ let c = document.createElement("ul");
+ l.forEach(e => c.appendChild(e));
+ UTIL.hidden(c);
+ return c;
+ }
+
+ dom.makeMethod = (node, l) => {
+ let m = node.id;
+ console.log(`load method ${m}`);
+ node.appendChild(dom.makeList(l[m]));
+ }
+
+ dom.loadPhoto = (p) => {
+ UTIL.removeAll(dom.photo_dom);
+ console.log("load picture " + p.name);
+ dom.photo_dom.appendChild(p.img);
+ UTIL.un_hidden(dom.photo_dom);
+ }
+
+ dom.init = () => {
+ // methods
+ Array.from(dom.meth_dom.children)
+ .forEach(e => {
+ dom.makeMethod(e, dom.meth_list);
+ e.addEventListener("click", () => {
+ event.stopPropagation();
+ CLICK.method(e);
+ });
+ });
+ }
+
+ return dom;
+})(DOM || {});
--- /dev/null
+(() => {
+"use strict";
+ //// json handling ////
+ function loadJson(text) {
+ let json = JSON.parse(text);
+ PHOTO.all = json.photos;
+ PHOTO.all.forEach(p => {
+ p.thm = DOM.makeThumbnail(p);
+ p.img = DOM.makePicture(p);
+ });
+
+ PHOTO.methods.forEach(m => {
+ let a = [];
+ json[m].forEach(s => a.push(DOM.makeCategory(s, m)));
+ DOM.meth_list[m] = a;
+ });
+ }
+
+ ///// input handling /////
+ function keyDown(e) {
+ if (e.keyCode === 27 || e.keyCode === 32) {
+ DOM.photo_dom.classList.add("hidden");
+ } else {
+ console.log(`${e.code} -> ${e.keyCode}`);
+ }
+ }
+
+ document.addEventListener("DOMContentLoaded",
+ (event) => {
+ let xhttp = new XMLHttpRequest();
+ xhttp.onload = (() => {
+ loadJson(xhttp.responseText);
+ PHOTO.updateThumbs();
+ DOM.init();
+ });
+ xhttp.open("GET", "data/pictures.json");
+ xhttp.send();
+
+ DOM.thumb_dom = document.getElementById("thumbs");
+ DOM.meth_dom = document.getElementById("methods");
+ DOM.photo_dom = document.getElementById("photo");
+
+ DOM.photo_dom.addEventListener("click", () => {
+ DOM.photo_dom.classList.add("hidden");
+ });
+
+ document.addEventListener("keydown", keyDown);
+ });
+})();
--- /dev/null
+var PHOTO = (photo => {
+ photo.methods = [ "loc", "date", "sub" ];
+ photo.all;
+ photo.shown;
+ photo.filter_groups
+ = new Map(photo.methods.map(s => [s, new Map([["T", UTIL.top]])]));
+
+ photo.updateThumbs = () => {
+ UTIL.removeAll(DOM.thumb_dom);
+ let pred = p => Array.from(photo.filter_groups.values()).every(
+ m => Array.from(m.values()).some(f => f(p)));
+ photo.shown = photo.all.filter(pred);
+ photo.shown.forEach(p => DOM.thumb_dom.appendChild(p.thm));
+ }
+
+ photo.addFilter = (m, s, f) => {
+ let g = photo.filter_groups.get(m);
+ g.delete("T");
+ g.set(s, f);
+ photo.updateThumbs();
+ }
+
+ // returns true iff last filter removed
+ photo.removeFilter = (m, s) => {
+ let g = photo.filter_groups.get(m);
+ let r = false;
+ if (g.delete(s)) {
+ if (g.size === 0) {
+ g.set("T", UTIL.top);
+ r = true;
+ }
+ photo.updateThumbs();
+ }
+ return r;
+ }
+
+ return photo;
+})(PHOTO || {});
--- /dev/null
+var UTIL = (util => {
+ util.top = _ => true;
+ util.siblings = (n) => Array.from(n.parentNode.children);
+ util.peek = (a) => a[a.length-1];
+
+ util.removeLast = (e) => e.removeChild(e.lastChild);
+ util.removeAll = (e) => {
+ while(e.lastChild) {
+ e.removeChild(e.lastChild);
+ }
+ }
+
+ let classes = ["hidden", "selected", "deselected", "normal"];
+ classes.forEach(c => {
+ util[c] = (e) => e.classList.add(c);
+ util[`un_${c}`] = (e) => e.classList.remove(c);
+ util[`is_${c}`] = (e) => e.classList.contains(c);
+ });
+
+ return util;
+})(UTIL || {});
.parent { height: 95%; }
.deselected { color: #797777; }
.selected { color: #e3e2e2; }
+.normal { color: #c3c2c2; }
.hidden { display: none; }
body {
background-color: #161516;
color: #c3c2c2;
margin: 5%;
- font-size: larger;
+ font-size: 2vw;
font-family: sans-serif;
}
}
li {
- line-height: 1.8;
+ padding: 1vw;
}
img {
.grid {
display: grid;
- grid-template-columns: 15vw 70vw;
- grid-column-gap: 5vw;
+ grid-template-columns: 15vw 0.8fr;
+ grid-column-gap: 3vw;
}
.menu {
}
.thumb-grid {
- max-width: 1200px;
- margin: 0 auto;
display: grid;
- grid-gap: 1rem;
- grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
+ grid-gap: 1vw;
+ grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
}
-.thumb-tile {
- display: inline grid;
+.thumb-grid > div {
+ display: flex;
+ height: 160px;
+ width: 160px;
+ justify-content: center;
+ align-items: center;
}
-.thumb-tile > img {
+.thumb-grid > div > img {
+ display: block;
max-height: 160px;
max-width: 160px;
border-radius: 10px;
box-shadow: 5px 5px 5px #050404;
+ /* margin: auto; */
}
-.thumb-tile > img:hover {
+.thumb-grid > div > img:hover {
animation-name: bounce;
animation-timing-function: ease-in-out;
animation-duration: 300ms;
transform: translate(0px, -2.5px);
}
-.thumb-tile > img:active {
+.thumb-grid > div > img:active {
animation-name: press;
animation-timing-function: ease-in-out;
animation-duration: 100ms;
transform: translate(0px, -2.5px);
}
100% {
- box-shadow: 2px 2px 5px #050404;
- transform: translate(0px, 1px);
+ box-shadow: 2px 2px 5px #050404;
+ transform: translate(0px, 1px);
}
}
box-shadow: 5px 5px 5px #050404;
transform: translate(0px, 0px);
}
- 20% {
- box-shadow: 8px 8px 5px #050404;
- transform: translate(0px, -1.0px);
- }
- 40% {
- box-shadow: 10px 10px 5px #050404;
- transform: translate(0px, -2.5px);
- }
- 60% {
- box-shadow: 12px 12px 5px #050404;
- transform: translate(0px, -4.0px);
- }
- 80% {
- box-shadow: 8px 8px 5px #050404;
- transform: translate(0px, -1.0px);
- }
100% {
box-shadow: 10px 10px 5px #050404;
transform: translate(0px, -2.5px);