summaryrefslogtreecommitdiff
path: root/static
diff options
context:
space:
mode:
Diffstat (limited to 'static')
-rw-r--r--static/index.css91
-rw-r--r--static/index.html13
-rw-r--r--static/index.js196
3 files changed, 300 insertions, 0 deletions
diff --git a/static/index.css b/static/index.css
new file mode 100644
index 0000000..1d1c542
--- /dev/null
+++ b/static/index.css
@@ -0,0 +1,91 @@
+body {
+ font-family: sans-serif;
+}
+
+#list {
+ font-size: 30px;
+}
+
+.item {
+ margin-bottom: 4px;
+}
+
+.item.flash {
+ background-color: #fdd;
+}
+
+.item.negative {
+ color: #999;
+}
+
+.item.make-new {
+ margin-top: 7px;
+}
+
+.item .item-buttons {
+ display: none;
+ user-select: none;
+}
+.item:hover .item-buttons {
+ display: inline;
+}
+
+.item-bullet {
+ color: #444;
+ user-select: none;
+ margin-right: 10px;
+}
+.item.negative .item-bullet {
+ color: #aaa;
+}
+
+.item-votes {
+ display: inline-block;
+ font-size: 18px;
+ color: #444;
+ vertical-align: 3px;
+ margin-left: 15px;
+}
+
+.item-upvote {
+ border: 1px #888 solid;
+ border-radius: 4px;
+ background-color: #eee;
+ color: green;
+ font-weight: bold;
+ cursor: pointer;
+ margin-left: 15px;
+ padding-left: 8px;
+ padding-right: 8px;
+}
+
+.item-downvote {
+ font-size: 23px;
+ color: blue;
+ cursor: pointer;
+ margin-left: 15px;
+ vertical-align: 2px;
+}
+
+.item-delete {
+ font-weight: bold;
+ font-family: mononoki;
+ color: #f00;
+ font-size: 28px;
+ margin-left: 11px;
+ cursor: pointer;
+}
+
+.new-item-bullet {
+ color: #888;
+ font-weight: bold;
+ font-family: mononoki;
+ margin-left: 1px;
+ margin-right: 8px;
+ user-select: none;
+ vertical-align: 1px;
+}
+
+.new-item-input {
+ font-size: 26px;
+}
diff --git a/static/index.html b/static/index.html
new file mode 100644
index 0000000..67ba2f8
--- /dev/null
+++ b/static/index.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>List</title>
+<script src="/socket.io/socket.io.js"></script>
+<script src="/index.js"></script>
+<link rel="stylesheet" href="/index.css">
+</head>
+<body>
+ <div id="list"></div>
+</body>
+</html>
diff --git a/static/index.js b/static/index.js
new file mode 100644
index 0000000..23d53ea
--- /dev/null
+++ b/static/index.js
@@ -0,0 +1,196 @@
+var socket = null;
+var is_initialised = false;
+var glist = []; // [(int, string, element)]
+var el_list = null;
+
+function setupSocket() {
+ socket = io();
+
+ socket.on("init", function(list) {
+ // Reload if server restarted
+ if (!is_initialised) is_initialised = true;
+ else { location.reload(); return; }
+
+ glist = list;
+ for (var i = 0; i < glist.length; i++) glist[i].push(null);
+ initialiseList();
+ });
+
+ socket.on("error", function(err) {
+ alert("Error: " + err);
+ });
+
+ socket.on("alert", function(text) {
+ alert(text);
+ });
+}
+
+// Returns element
+function createItemElement(votes, string) {
+ var el;
+
+ var el_item = document.createElement("div");
+ el_item.classList.add("item");
+ if (votes < 0) el_item.classList.add("negative");
+
+ el = document.createElement("span");
+ el.classList.add("item-bullet");
+ el.appendChild(document.createTextNode("•"));
+ el_item.appendChild(el);
+
+ el = document.createElement("span");
+ el.classList.add("item-label");
+ el.appendChild(document.createTextNode(string));
+ el_item.appendChild(el);
+
+ el = document.createElement("span");
+ el.classList.add("item-votes");
+ el.appendChild(document.createTextNode("[" + votes + "]"));
+ el_item.appendChild(el);
+
+ var buttons = document.createElement("span");
+ buttons.classList.add("item-buttons");
+
+ el = document.createElement("span");
+ el.classList.add("item-upvote");
+ el.appendChild(document.createTextNode("⇧"));
+ el.addEventListener("click", function() {
+ upvoteItem(string, 1);
+ });
+ buttons.appendChild(el);
+
+ el = document.createElement("span");
+ el.classList.add("item-downvote");
+ el.appendChild(document.createTextNode("↓"));
+ el.addEventListener("click", function() {
+ upvoteItem(string, -1);
+ });
+ buttons.appendChild(el);
+
+ el = document.createElement("span");
+ el.classList.add("item-delete");
+ el.appendChild(document.createTextNode("⨯"));
+ el.addEventListener("click", function() {
+ if (votes < 0) deleteItem(string);
+ else negateItem(string);
+ });
+ buttons.appendChild(el);
+
+ el_item.appendChild(buttons);
+
+ return el_item;
+}
+
+function initialiseList() {
+ var el_item, el;
+ el_list.innerHTML = "";
+
+ for (var i = 0; i < glist.length; i++) {
+ el_item = createItemElement(glist[i][0], glist[i][1]);
+ glist[i][2] = el_item;
+ el_list.appendChild(el_item);
+ }
+
+ el_item = document.createElement("div");
+ el_item.classList.add("item");
+ el_item.classList.add("make-new");
+
+ el = document.createElement("span");
+ el.classList.add("new-item-bullet");
+ el.appendChild(document.createTextNode("+"));
+ el_item.appendChild(el);
+
+ el = document.createElement("input");
+ el.type = "text";
+ el.setAttribute("size", 21);
+ el.setAttribute("placeholder", "New item...");
+ el.classList.add("new-item-input");
+ el.addEventListener("keypress", function(ev) {
+ if (ev.key == "Enter" || ev.keyCode == 10 || ev.keyCode == 13) {
+ submitNewItem(ev.target.value);
+ ev.target.value = "";
+ }
+ });
+ el_item.appendChild(el);
+
+ el_list.appendChild(el_item);
+}
+
+function submitNewItem(string) {
+ for (var i = 0; i < glist.length; i++) {
+ if (glist[i][1] == string) {
+ var el = glist[i][2];
+ el.classList.add("flash");
+ setTimeout(function(i) { el.classList.remove("flash"); }, 200);
+ return;
+ }
+ }
+
+ socket.emit("new", string);
+
+ insertItem(0, string);
+}
+
+function upvoteItem(string, incr) {
+ for (var i = 0; i < glist.length; i++) {
+ if (glist[i][1] == string) {
+ glist[i][0] += incr;
+ socket.emit("set_votes", string, glist[i][0]);
+ reinsertItem(i);
+ break;
+ }
+ }
+}
+
+function negateItem(string) {
+ for (var i = 0; i < glist.length; i++) {
+ if (glist[i][1] == string) {
+ glist[i][0] = -1;
+ socket.emit("set_votes", string, -1);
+ reinsertItem(i);
+ break;
+ }
+ }
+}
+
+function deleteItem(string) {
+ for (var i = 0; i < glist.length; i++) {
+ if (glist[i][1] == string) {
+ socket.emit("delete", string);
+ glist[i][2].parentElement.removeChild(glist[i][2]);
+ glist.splice(i, 1);
+ break;
+ }
+ }
+}
+
+function insertItem(votes, string) {
+ var i;
+ for (i = 0; i < glist.length; i++) {
+ if (glist[i][0] > votes) continue;
+ if (glist[i][0] < votes || glist[i][1] > string) break;
+ }
+ var el = createItemElement(votes, string);
+ if (i == glist.length) {
+ glist.push([votes, string, el]);
+ el_list.insertBefore(el, document.querySelector(".item.make-new"));
+ } else {
+ var refel = glist[i][2];
+ glist.splice(i, 0, [votes, string, el]);
+ el_list.insertBefore(el, refel);
+ }
+}
+
+function reinsertItem(index) {
+ glist[index][2].parentElement.removeChild(glist[index][2]);
+ var votes = glist[index][0];
+ var string = glist[index][1];
+ glist.splice(index, 1);
+
+ insertItem(votes, string);
+}
+
+window.addEventListener("load", function() {
+ el_list = document.getElementById("list");
+ setupSocket();
+});