diff --git a/app/network.js b/app/network.js new file mode 100644 index 0000000..c00b598 --- /dev/null +++ b/app/network.js @@ -0,0 +1,89 @@ +// Functionality dealing with server-level things + +var maxScrollback = 500; +var networks = {}; + +function networkConnect(network, baseURL, authtok) { + var eventSource; + var element = getTemplate("server-channels"); + var channels = element.getElementByClassName("channels")[0]; + var roomElement = element.getElementsByClassName("server")[0]; + var rooms = {".": room}; + newRoom(roomElement, element, network, maxScrollback); + + function newRoom(name) { + var rElement = getTemplate("channel"); + newRoom(rElement, element, name, maxScrollback); + channels.appendChild(rElement); + rooms[name] = rElement; + } + + function handleEventSourceLine(line) { + var lhs = line.split(" :", 1)[0] + var parts = lhs.split(' ') + var timestamp = new Date(parts[0] * 1000); + var fullSender = parts[1]; + var command = parts[2].toLowerCase(); + var sender = parts[3]; + var forum = parts[4]; + var args = parts.slice(5); + var txt = line.substr(lhs.length + 2); + + var room = rooms[forum]; + if (! room) { + room = newRoom(forum); + } + + // XXX: Handle differently based on command + room.addMessage(timestamp, command, sender, txt); + } + + function handleEventSourceMessage(oEvent) { + msgs = oEvent.data.split("\n"); + + var first = Math.max(0, msgs.length - maxScrollback); + for (var i = first; i < msgs.length; i += 1) { + handleEventSourceLine(msgs[i]); + } + } + + function handleEventSourceError(oEvent) { + timestamp = new Date(); + messageHandler(timestamp, null, "ERROR", null, null, [], null); + } + + element.send = function(target, text) { + function handleError(oEvent) { + console.log("XXX: That didn't work out.", target, text) + } + + var form = new FormData(); + form.append("type", "command"); + form.append("auth", authtok); + form.append("network", network); + form.append("target", target); + form.append("text", text); + console.log(form); + + var oReq = new XMLHttpRequest(); + oReq.addEventListener("error", handleError); + oReq.open("POST", baseURL, true); + oReq.send(form); + } + + element.close = function() { + eventSource.close(); + element.parentNode.removeChild(element); + } + + + if (networks[network]) { + networks[network].close(); + } + networks[network] = element; + + var pullURL = baseURL + "?network=" + encodeURIComponent(network) + "&auth=" + encodeURIComponent(authtok); + eventSource = new EventSource(pullURL); + eventSource.addEventListener("message", handleEventSourceMessage); + eventSource.addEventListener("error", handleEventSourceError); +} diff --git a/app/room.js b/app/room.js new file mode 100644 index 0000000..a741263 --- /dev/null +++ b/app/room.js @@ -0,0 +1,113 @@ +function djbhash(a) { + var r = 5381; + + for (var i = 0; i < a.length; i += 1) { + r = (((r << 5) + r) + a.charCodeAt(i)) & 0xffff; + } + return r; +} + +function addText(p, text, kiboze) { + // Look for a URL + var txtElement = document.createElement("span"); + txtElement.className = "text"; + var rhs = text; + var match; + + while ((match = urlRe.exec(rhs)) != null) { + var before = rhs.substr(0, match.index); + var a = document.createElement("a"); + var href = match[0]; + + if (href.indexOf("hxx") == 0) { + href = "htt" + href.substr(3); + } + a.href = href + a.target = "_blank"; + a.appendChild(document.createTextNode(match[0])); + txtElement.appendChild(document.createTextNode(before)); + txtElement.appendChild(a); + rhs = rhs.substr(match.index + match[0].length); + } + txtElement.appendChild(document.createTextNode(rhs)); + p.appendChild(txtElement); + + if ((kiboze) || (-1 != text.search(kibozeRe))) { + var k = document.getElementById("kiboze"); + var p2 = p.cloneNode(true); + + if (k) { + k.insertBefore(p2, k.firstChild); + p2.onclick = function() { focus(p); } + + // Setting title makes the tab flash sorta + document.title = document.title; + } + } +} + +var visibleRoom; + +function newRoom(element, network, name, maxSize) { + var messages = getTemplate("messages"); + if (! maxsize) { + maxSize = 500; + } + + element.addMessage = function(timestamp, command, source, content) { + var message = getTemplate("message"); + + var eTimestamp = message.getElementsByClassName("timestamp")[0]; + var eSource = message.getElementsByClassName("source")[0]; + var eContent = message.getElementsByClassName("content")[0]; + + message.nodeList.add(command); + if (source = ".") { + message.nodeList.add("self"); + } + + eTimestamp.textContent = timestamp.toLocaleTimeString(); + eSource.textContent = source; + eSource.setAttribute("colornumber", djbhash(source) % 31); + eContent.textContent = content; + + messages.appendChild(message); + + while (messages.childNodes.length > maxSize) { + messages.removeChild(element.firstChild); + } + + if (visibleRoom == element) { + message.scrollIntoView(false); + } + } + + element.hide = function() { + element.classList.remove("selected"); + messages.classList.add("hidden"); + } + + element.show = function() { + if (visibleRoom) { + visibleRoom.hide() + } + element.classList.add("selected"); + messages.classList.remove("hidden"); + visibleRoom = element; + } + + element.send = function(text) { + network.send(name, text); + } + + function clicked() { + element.show(); + } + + // start hidden + element.hide(); + element.addEventListener("click", clicked); + element.getElementsByClassName("content-item")[0].textContent = name; + + return element; +} \ No newline at end of file diff --git a/app/server.js b/app/server.js deleted file mode 100644 index bf2c081..0000000 --- a/app/server.js +++ /dev/null @@ -1,67 +0,0 @@ -// Functionality dealing with server-level things - -var maxScrollback = 500; - -function Server(network, baseURL, authtok, messageHandler) { - function init() { - var pullURL = baseURL + "?network=" + encodeURIComponent(network) + "&auth=" + encodeURIComponent(authtok); - this.eventSource = new EventSource(pullURL); - this.eventSource.addEventListener("message", handleEventSourceMessage); - this.eventSource.addEventListener("error", handleEventSourceError); - } - - function close() { - this.eventSource.close(); - } - - function handleEventSourceLine(line) { - var lhs = line.split(" :", 1)[0] - var parts = lhs.split(' ') - var timestamp = new Date(parts[0] * 1000); - var fullSender = parts[1]; - var command = parts[2]; - var sender = parts[3]; - var forum = parts[4]; - var args = parts.slice(5); - var txt = line.substr(lhs.length + 2); - - messageHandler(timestamp, fullSender, command, sender, forum, args, txt); - } - - function handleEventSourceMessage(oEvent) { - msgs = oEvent.data.split("\n"); - - var first = Math.max(0, msgs.length - maxScrollback); - for (var i = first; i < msgs.length; i += 1) { - handleEventSourceLine(msgs[i]); - } - } - - function handleEventSourceError(oEvent) { - timestamp = new Date(); - messageHandler(timestamp, null, "ERROR", null, null, [], null); - } - - function send(target, text) { - function handleError(oEvent) { - console.log("XXX: That didn't work out.", target, text) - } - - var form = new FormData(); - form.append("type", "command"); - form.append("auth", authtok); - form.append("network", network); - form.append("target", target); - form.append("text", text); - console.log(form); - - var oReq = new XMLHttpRequest(); - oReq.addEventListener("error", handleError); - oReq.open("POST", baseURL, true); - oReq.send(form); - } - - this.send = send; - - init(); -} diff --git a/app/wirc.html b/app/wirc.html index 8175e83..c383fb5 100644 --- a/app/wirc.html +++ b/app/wirc.html @@ -6,7 +6,8 @@ - + + diff --git a/app/wirc.js b/app/wirc.js index 25c35bd..03a69df 100644 --- a/app/wirc.js +++ b/app/wirc.js @@ -4,27 +4,12 @@ var urlRe = /[a-z]+:\/\/[^ ]*/; var nick = "Mme. M"; -// XXX: get rid of this -var scrollbackLength = 500; -var current; -var target; - if (String.prototype.startsWith == null) { String.prototype.startsWith = function(needle) { return this.lastIndexOf(needle, 0) == 0; } } -function djbhash(a) { - var r = 5381; - - for (var i = 0; i < a.length; i += 1) { - r = (((r << 5) + r) + a.charCodeAt(i)) & 0xffff; - } - return r; -} - - function getTemplate(className) { return templates.getElementsByClassName(className)[0].cloneNode(true); } @@ -33,50 +18,6 @@ function isinView(oObject) { return (oObject.offsetParent.clientHeight <= oObject.offsetTop); } -function selectForum(room) { - if (current) { - current.classList.remove("selected"); - // XXX: do this with a class, too - current.messages.style.display = "none"; - } - - - current = room; - target = room.target; - room.classList.add("selected"); - room.messages.style.display = "block"; - - if (room.messages.lastChild) { - room.messages.lastChild.scrollIntoView(false); - } -} - -fora = {} -function getForumElement(forum) { - var fe = fora[forum]; - - if (! fe) { - var room = getTemplate("channel room"); - var content = room.getElementsByClassName("content-item")[0]; - - content.textContent = forum; - rooms.appendChild(room); - - fe = getTemplate("messages"); - fe.room = room; - - room.messages = fe; - room.target = forum; - // XXX: split out into non-anon function - room.addEventListener("click", function() {selectForum(room)}); - - fora[forum] = fe; - document.getElementById("messages-container").appendChild(fe); - } - - return fe; -} - function addMessagePart(p, className, text) { var e = document.createElement("span"); e.className = className; @@ -85,45 +26,6 @@ function addMessagePart(p, className, text) { p.appendChild(document.createTextNode(" ")); } -function addText(p, text, kiboze) { - // Look for a URL - var txtElement = document.createElement("span"); - txtElement.className = "text"; - var rhs = text; - var match; - - while ((match = urlRe.exec(rhs)) != null) { - var before = rhs.substr(0, match.index); - var a = document.createElement("a"); - var href = match[0]; - - if (href.indexOf("hxx") == 0) { - href = "htt" + href.substr(3); - } - a.href = href - a.target = "_blank"; - a.appendChild(document.createTextNode(match[0])); - txtElement.appendChild(document.createTextNode(before)); - txtElement.appendChild(a); - rhs = rhs.substr(match.index + match[0].length); - } - txtElement.appendChild(document.createTextNode(rhs)); - p.appendChild(txtElement); - - if ((kiboze) || (-1 != text.search(kibozeRe))) { - var k = document.getElementById("kiboze"); - var p2 = p.cloneNode(true); - - if (k) { - k.insertBefore(p2, k.firstChild); - p2.onclick = function() { focus(p); } - - // Setting title makes the tab flash sorta - document.title = document.title; - } - } -} - function focus(e) { var pct = 1; var timeout; @@ -142,49 +44,6 @@ function focus(e) { }, 50) } -function addMessage(timestamp, fullSender, command, sender, forum, args, msg) { - var forumElement = getForumElement(forum); - var msge = getTemplate("message"); - - msge.classList.add("update"); - msge.classList.add("privmsg"); - - if (sender == ".") { - msge.classList.add("self"); - } - - console.log(timestamp, msg); - - msge.getElementsByClassName("timestamp")[0].textContent = timestamp.toLocaleTimeString(); - var sourcee = msge.getElementsByClassName("source")[0]; - var contente = msge.getElementsByClassName("content")[0]; - - var senderhash = djbhash(sender) % 31; - sourcee.setAttribute("colornumber", senderhash) - - - sourcee.textContent = sender; - - switch (command) { - case "PING": - case "PONG": - return; - case "PRIVMSG": - case "NOTICE": - addText(contente, msg); - break; - default: - contente.textContent = command + " " + args + " " + msg; - break; - } - while (forumElement.childNodes.length > scrollbackLength) { - forumElement.removeChild(forumElement.firstChild) - } - - forumElement.appendChild(msge); - msge.scrollIntoView(false); -} - function handleInput(oEvent) { var txt = oEvent.target.value; if (txt.startsWith("/connect ")) { @@ -198,7 +57,7 @@ function handleInput(oEvent) { storedConnections[network] = [url, authtok]; chrome.storage.sync.set({"connections": storedConnections}); } else { - server.send(target, txt); + visibleRoom.send(txt); } oEvent.target.value = ""; @@ -206,42 +65,13 @@ function handleInput(oEvent) { return false; } -var server; -var activeNetworks = {}; -var storedConnections = {}; - -function connect(network, url, authtok) { - var newServer = new Server(network, url, authtok, addMessage); - var element; - - if (activeNetworks[network]) { - activeNetworks[network].close(); - element = activeNetworks[network].element; - } else { - newServer.element = getTemplate("server-channels"); - rooms.appendChild(newServer.element); - } - - newServer.room = newServer.element.getElementsByClassName("server room")[0]; - newServer.content = newServer.element.getElementsByClassName("content-item")[0]; - - newServer.content.textContent = network; - - activeNetworks[network] = newServer; - - // XXX: this should be bound to the element - server = newServer; -} - - - function restore(items) { storedConnections = items["connections"]; for (var network in storedConnections) { var conn = storedConnections[network]; - connect(network, conn[0], conn[1]); + networkConnect(network, conn[0], conn[1]); } }