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 if (image.censored) {
182 img.style.display = "none";
185 if (image.prompt && image.code) {
186 const figcaption = document.createElement('figcaption');
187 const caption_text = document.createTextNode(`${image.prompt} (${image.code}) `);
188 figcaption.appendChild(caption_text);
190 const reuse_button = document.createElement('button');
191 reuse_button.appendChild(document.createTextNode("Reuse"));
192 figcaption.appendChild(reuse_button);
194 reuse_button.addEventListener('click', () => {
195 prompt.value = image.prompt;
196 window.scrollTo(0,0);
199 if (image.censored) {
200 figcaption.style.display = "none";
203 figure.appendChild(figcaption);
206 const dl_comments = document.createElement('dl');
207 dl_comments.className = "comments";
208 dl_comments.id = "comments_" + image.id;
210 image.comments.forEach((comment) => {
211 add_comment(dl_comments, comment);
214 figure.appendChild(dl_comments);
216 const comment_form = document.createElement('form');
217 const comment_input = document.createElement('input')
218 comment_input.type = "text";
219 comment_input.className = "comment";
220 comment_input.placeholder = "Add a comment";
221 comment_form.appendChild(comment_input);
222 figure.appendChild(comment_form);
224 comment_input.addEventListener('focus', () => {
225 /* If the server has informed us it has our name, we are done. */
229 /* If server has no name, check local storage. */
230 const name = localStorage.getItem('name');
232 socket.emit('set-name', name);
236 /* Failing both, bring up the modal dialog to set the name. */
237 name_dialog.showModal();
240 comment_form.addEventListener('submit', function(e) {
242 if (comment_input.value) {
243 socket.emit('comment', {image_id: image.id,
244 text: comment_input.value});
245 comment_input.value = '';
249 images.prepend(figure);
252 function hide_spinner() {
253 zombo_form.style.display = "grid";
254 spinner.style.display = "none";
257 zombo_form.addEventListener('submit', function(e) {
259 /* Hide the form and show spinner while generation is happening. */
260 zombo_form.style.display = "none";
261 spinner.style.display = "block";
262 spinner_timeout = setTimeout(hide_spinner, 60000);
263 socket.emit('generate', {"prompt": prompt.value, "code": code.value});
267 socket.on('generation-done', () => {
268 /* Re-display the form and hide spinner now that generation is over. */
269 clearTimeout(spinner_timeout);
273 function random_from(items) {
274 return items[Math.floor(Math.random()*items.length)];
277 safety.addEventListener("click", () => {
280 "A hyperrealistic photograph of",
281 "A matte painting of",
282 "Epic fantasy card art of",
283 "Professional oil painting of",
285 "A pencil sketch of",
288 "A marble statue of",
293 " a Porsche 911 Carrera",
297 " an elfin princess",
299 " pastel purple clouds",
300 " a Teenage Mutant Ninja Turtle",
304 " in tropical surroundings",
306 " in an ocean storm",
307 " in a snowy forest",
308 " in a cardboard box",
310 " in an epic landscape",
311 " in a haunted forest",
312 " riding a motorbike",
313 " in a futuristic city at night",
314 " in a dystopian landscape, foggy",
315 " in the land of snow",
316 " that looks like a watermelon",
317 " on the streets of London at night with neon lights flashing",
318 " that is part octopus",
319 " melting into a puddle",
320 " on a birthday cake",
326 " by Vincent Van Gogh",
328 " by Johannes Vermeer",
329 ", fantasy art, neon fog",
330 ", epic lighting from above",
331 ", trending on artstation",
333 " by Leonardo da Vinci",
336 prompt.value = random_from(art_type) + random_from(subject) +
337 random_from(scenery) + random_from(artist);