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;
179 figure.appendChild(img);
181 const figcaption = document.createElement('figcaption');
182 const caption_text = document.createTextNode(`${image.prompt} (${image.code}) `);
183 figcaption.appendChild(caption_text);
185 const reuse_button = document.createElement('button');
186 reuse_button.appendChild(document.createTextNode("Reuse"));
187 figcaption.appendChild(reuse_button);
189 reuse_button.addEventListener('click', () => {
190 prompt.value = image.prompt;
191 window.scrollTo(0,0);
194 figure.appendChild(figcaption);
196 const dl_comments = document.createElement('dl');
197 dl_comments.className = "comments";
198 dl_comments.id = "comments_" + image.id;
200 image.comments.forEach((comment) => {
201 add_comment(dl_comments, comment);
204 figure.appendChild(dl_comments);
206 const comment_form = document.createElement('form');
207 const comment_input = document.createElement('input')
208 comment_input.type = "text";
209 comment_input.className = "comment";
210 comment_input.placeholder = "Add a comment";
211 comment_form.appendChild(comment_input);
212 figure.appendChild(comment_form);
214 comment_input.addEventListener('focus', () => {
215 /* If the server has informed us it has our name, we are done. */
219 /* If server has no name, check local storage. */
220 const name = localStorage.getItem('name');
222 socket.emit('set-name', name);
226 /* Failing both, bring up the modal dialog to set the name. */
227 name_dialog.showModal();
230 comment_form.addEventListener('submit', function(e) {
232 if (comment_input.value) {
233 socket.emit('comment', {image_id: image.id,
234 text: comment_input.value});
235 comment_input.value = '';
239 images.prepend(figure);
242 function hide_spinner() {
243 zombo_form.style.display = "grid";
244 spinner.style.display = "none";
247 zombo_form.addEventListener('submit', function(e) {
249 /* Hide the form and show spinner while generation is happening. */
250 zombo_form.style.display = "none";
251 spinner.style.display = "block";
252 spinner_timeout = setTimeout(hide_spinner, 60000);
253 socket.emit('generate', {"prompt": prompt.value, "code": code.value});
257 socket.on('generation-done', () => {
258 /* Re-display the form and hide spinner now that generation is over. */
259 clearTimeout(spinner_timeout);
263 function random_from(items) {
264 return items[Math.floor(Math.random()*items.length)];
267 safety.addEventListener("click", () => {
270 "A hyperrealistic photograph of",
271 "A matte painting of",
272 "Epic fantasy card art of",
273 "Professional oil painting of",
275 "A pencil sketch of",
278 "A marble statue of",
283 " a Porsche 911 Carrera",
287 " an elfin princess",
289 " pastel purple clouds",
290 " a Teenage Mutant Ninja Turtle",
294 " in tropical surroundings",
296 " in an ocean storm",
297 " in a snowy forest",
298 " in a cardboard box",
300 " in an epic landscape",
301 " in a haunted forest",
302 " riding a motorbike",
303 " in a futuristic city at night",
304 " in a dystopian landscape, foggy",
305 " in the land of snow",
306 " that looks like a watermelon",
307 " on the streets of London at night with neon lights flashing",
308 " that is part octopus",
309 " melting into a puddle",
310 " on a birthday cake",
316 " by Vincent Van Gogh",
318 " by Johannes Vermeer",
319 ", fantasy art, neon fog",
320 ", epic lighting from above",
321 ", trending on artstation",
323 " by Leonardo da Vinci",
326 prompt.value = random_from(art_type) + random_from(subject) +
327 random_from(scenery) + random_from(artist);