5 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
7 <link href="/zombo.css" rel="stylesheet" type="text/css">
8 <meta name="viewport" content="width=device-width,initial-scale=1">
9 <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
10 <meta name="HandheldFriendly" content="true">
16 <div id="header" align="center">
22 <img src="/zombocom.png" alt="Zombocom" longdesc="http://zombo.com" width="1199" height="217">
27 Welcome to Zombocom. You can do anything at Zombocom, anything at
28 all. The only limit is yourself!
31 <dialog id="name-dialog">
32 <form method="dialog">
35 <input id="name" type="text" autocomplete="off">
39 <button value="default">OK</button>
45 <div id="spinner" align="center">
46 <div class="animate-flicker">
48 <img src="/pngwheel.png" class="rotate thefade">
53 <form action="" id="zombo-form">
54 <div class="form-row large">
58 <button id="safety" type="button">Safety prompt</button>
59 <textarea id="prompt" rows="4" width="100%" autocomplete="off" required></textarea>
62 <div class="form-row small left">
63 <div class="labeled-row">
67 <input id="code" type="number" min="0" max="4294967295" autocomplete="off" placeholder="(Leave blank for random)" />
71 <div class="form-row large">
72 <button id="generate" type="submit">Make the infinite possible</button>
80 <audio loop="" src="/zombo_words.mp3" type="audio/mpeg"></audio>
81 <button id="mute" class="menu-button fade volume">
84 const mute = document.querySelector("#mute");
85 const icon = document.querySelector("#mute > div");
86 const audio = document.querySelector("audio");
88 mute.addEventListener("click", () => {
92 icon.innerHTML = "🔈";
95 icon.innerHTML = "🔊";
97 mute.classList.add("fade");
103 <button id="profile" class="menu-button">
109 <script src="/socket.io/socket.io.js"></script>
113 const name_input = document.querySelector("#name");
114 const name_dialog = document.querySelector("#name-dialog");
116 const images = document.querySelector("#images");
117 const zombo_form = document.querySelector("#zombo-form");
118 const prompt = document.querySelector("#prompt");
119 const code = document.querySelector("#code");
120 const safety= document.querySelector("#safety");
121 const spinner = document.querySelector("#spinner");
122 const profile = document.querySelector("#profile");
125 profile.addEventListener('click', () => {
126 name_dialog.showModal();
129 name_dialog.addEventListener('close', () => {
130 socket.emit('set-name', name_input.value);
133 function add_comment(comments, comment) {
134 const dt = document.createElement('dt');
135 const dd = document.createElement('dd');
136 dt.textContent = comment.name + ':';
137 dd.textContent = comment.text;
138 comments.appendChild(dt);
139 comments.appendChild(dd);
142 socket.on('comment', function(comment) {
143 const comments = document.querySelector("#comments_" + comment.image_id);
144 add_comment(comments, comment);
147 socket.on('inform-name', (name) => {
148 console.log("Received inform-name event: " + name);
150 /* When we receive a name we store it in 3 places:
152 * * The informed_name variable (confirming what the server knows)
153 * * The name_input field (so the user will see that in their profile)
154 * * In localStorage (for use in a future session)
156 informed_name = name;
157 name_input.value = name;
158 localStorage.setItem('name', name);
161 socket.on('reset', () => {
162 images.replaceChildren();
164 /* Since the server is seeing us for the first time, let it
165 know our name (if we have one). */
166 const name = localStorage.getItem('name');
168 socket.emit('set-name', name);
173 socket.on('image', (image) => {
174 const figure = document.createElement('figure');
175 figure.id = "image_" + image.id;
177 const img = document.createElement('img');
178 img.src = image.filename;
181 const a = document.createElement('a');
184 figure.appendChild(a);
186 figure.appendChild(img);
189 if (image.censored) {
190 img.style.display = "none";
193 if (image.prompt && image.code) {
194 const figcaption = document.createElement('figcaption');
195 const caption_text = document.createTextNode(`${image.prompt} (${image.code}) `);
196 figcaption.appendChild(caption_text);
198 const reuse_button = document.createElement('button');
199 reuse_button.appendChild(document.createTextNode("Reuse"));
200 figcaption.appendChild(reuse_button);
202 reuse_button.addEventListener('click', () => {
203 prompt.value = image.prompt;
204 window.scrollTo(0,0);
207 if (image.censored) {
208 figcaption.style.display = "none";
211 figure.appendChild(figcaption);
214 const dl_comments = document.createElement('dl');
215 dl_comments.className = "comments";
216 dl_comments.id = "comments_" + image.id;
218 image.comments.forEach((comment) => {
219 add_comment(dl_comments, comment);
222 figure.appendChild(dl_comments);
224 const comment_form = document.createElement('form');
225 const comment_input = document.createElement('input')
226 comment_input.type = "text";
227 comment_input.className = "comment";
228 comment_input.placeholder = "Add a comment";
229 comment_form.appendChild(comment_input);
230 figure.appendChild(comment_form);
232 comment_input.addEventListener('focus', () => {
233 /* If the server has informed us it has our name, we are done. */
237 /* If server has no name, check local storage. */
238 const name = localStorage.getItem('name');
240 socket.emit('set-name', name);
244 /* Failing both, bring up the modal dialog to set the name. */
245 name_dialog.showModal();
248 comment_form.addEventListener('submit', function(e) {
250 if (comment_input.value) {
251 socket.emit('comment', {image_id: image.id,
252 text: comment_input.value});
253 comment_input.value = '';
257 images.prepend(figure);
260 function hide_spinner() {
261 zombo_form.style.display = "grid";
262 spinner.style.display = "none";
265 zombo_form.addEventListener('submit', function(e) {
267 /* Hide the form and show spinner while generation is happening. */
268 zombo_form.style.display = "none";
269 spinner.style.display = "block";
270 spinner_timeout = setTimeout(hide_spinner, 60000);
271 socket.emit('generate', {"prompt": prompt.value, "code": code.value});
275 socket.on('generation-done', () => {
276 /* Re-display the form and hide spinner now that generation is over. */
277 clearTimeout(spinner_timeout);
281 function random_from(items) {
282 return items[Math.floor(Math.random()*items.length)];
285 safety.addEventListener("click", () => {
288 "A hyperrealistic photograph of",
289 "A matte painting of",
290 "Epic fantasy card art of",
291 "Professional oil painting of",
293 "A pencil sketch of",
296 "A marble statue of",
301 " a Porsche 911 Carrera",
305 " an elfin princess",
307 " pastel purple clouds",
308 " a Teenage Mutant Ninja Turtle",
312 " in tropical surroundings",
314 " in an ocean storm",
315 " in a snowy forest",
316 " in a cardboard box",
318 " in an epic landscape",
319 " in a haunted forest",
320 " riding a motorbike",
321 " in a futuristic city at night",
322 " in a dystopian landscape, foggy",
323 " in the land of snow",
324 " that looks like a watermelon",
325 " on the streets of London at night with neon lights flashing",
326 " that is part octopus",
327 " melting into a puddle",
328 " on a birthday cake",
334 " by Vincent Van Gogh",
336 " by Johannes Vermeer",
337 ", fantasy art, neon fog",
338 ", epic lighting from above",
339 ", trending on artstation",
341 " by Leonardo da Vinci",
344 prompt.value = random_from(art_type) + random_from(subject) +
345 random_from(scenery) + random_from(artist);