${escapedMessage}
// Generated from Model/ChatItem.js // on Fri Jun 8 16:03:12 CEST 2018 // let rsrc_ChatItem_js = """ //===----------------------------------------------------------------------===// // // This source file is part of the swift-nio-irc open source project // // Copyright (c) 2018 ZeeZide GmbH. and the swift-nio-irc project authors // Licensed under Apache License v2.0 // // See LICENSE.txt for license information // See CONTRIBUTORS.txt for the list of SwiftNIOIRC project authors // // SPDX-License-Identifier: Apache-2.0 // //===----------------------------------------------------------------------===// // Our model object for something we display in a chat list const ChatItem = function(message, sender, time, isSystem, isRead) { this.message = message || ""; this.sender = sender || "✭"; this.time = time || new Date(); this.isSystem = isSystem || false; this.isRead = isRead || false; }; """ // Generated from Model/ClientConnection.js // on Fri Jun 8 16:03:12 CEST 2018 // let rsrc_ClientConnection_js = """ //===----------------------------------------------------------------------===// // // This source file is part of the swift-nio-irc open source project // // Copyright (c) 2018 ZeeZide GmbH. and the swift-nio-irc project authors // Licensed under Apache License v2.0 // // See LICENSE.txt for license information // See CONTRIBUTORS.txt for the list of SwiftNIOIRC project authors // // SPDX-License-Identifier: Apache-2.0 // //===----------------------------------------------------------------------===// /// This corresponds to the NIOIRC.IRCMessage, though we don't split out the /// IRCCommand value. /// Also: We use some non-standard commands ... const IRCMessage = function(origin, target, command, args) { this.time = new Date(); this.origin = origin || null; this.target = target || null; this.command = command || "X-MISSING-COMMAND"; this.arguments = args || new Array(); }; /// Wraps the WebSocket connection to the IRC bridge. const Connection = function(webSockEndPointURL, onMessage) { const self = this; self.onMessage = onMessage; self.connect = function(onConnect) { self.onConnect = onConnect; self.isFirstReceive = true; self.socket = new WebSocket(webSockEndPointURL); self.socket.onmessage = function(msg) { let ircMessage = null; try { const json = JSON.parse(msg.data); ircMessage = new IRCMessage(json["origin"], json["target"], json["command"], json["arguments"]) } catch(error) { console.error("failed to parse incoming message:", error); } self.onMessage(ircMessage); if (self.isFirstReceive) { if (self.onConnect !== undefined && ircMessage["command"] === "NICK") { // wait for 1st NICK self.isFirstReceive = false; self.onConnect(); } } } }; self.json = function(object) { self.socket.send(JSON.stringify(object)); }; self.call = function(command, ...args) { self.json({ "command": command, "arguments": args }); }; self.send = function(stringMessage) { // TODO: remove me self.call("NOTICE", "*", stringMessage); }; } """ // Generated from Model/ClientUtils.js // on Fri Jun 8 16:03:12 CEST 2018 // let rsrc_ClientUtils_js = """ //===----------------------------------------------------------------------===// // // This source file is part of the swift-nio-irc open source project // // Copyright (c) 2018 ZeeZide GmbH. and the swift-nio-irc project authors // Licensed under Apache License v2.0 // // See LICENSE.txt for license information // See CONTRIBUTORS.txt for the list of SwiftNIOIRC project authors // // SPDX-License-Identifier: Apache-2.0 // //===----------------------------------------------------------------------===// function formatDate(date) { const now = new Date(); if (now.getYear() != date.getYear() || now.getMonth() != date.getMonth() || now.getDate() != date.getDate()) return date.toString(); const m = date.getMinutes(); return `${date.getHours()}:${m < 10 ? "0" + m : m}` } function htmlEscape(str) { const div = document.createElement("div"); div.innerText = str; return div.innerHTML; } """ // Generated from ViewControllers/MainVC.js // on Fri Jun 8 16:03:12 CEST 2018 // let rsrc_MainVC_js = """ //===----------------------------------------------------------------------===// // // This source file is part of the swift-nio-irc open source project // // Copyright (c) 2018 ZeeZide GmbH. and the swift-nio-irc project authors // Licensed under Apache License v2.0 // // See LICENSE.txt for license information // See CONTRIBUTORS.txt for the list of SwiftNIOIRC project authors // // SPDX-License-Identifier: Apache-2.0 // //===----------------------------------------------------------------------===// const MainVC = function(nick, webSockEndPointURL) { const self = this; self.serverTarget = "server"; self.targets = { "server": Array() }; self.activeTarget = self.serverTarget; self.loadView = function() { const self = this; self.view = document.querySelector(".chat"); self.sidebar = new SidebarVC(self.onTargetChange); self.messages = new MessagesVC(self.onLine); self.sidebar.loadView(self.view); self.messages.loadView(self.view); }; self.systemNick = "✭"; self.nick = null; // nick is assigned by the server self.commandMap = { "/nick": function(name) { self.connection.call("NICK", name); }, "/join": function(target) { if (!target.startsWith("#")) target = "#" + target; const lc = target.toLowerCase(); self.connection.call("JOIN", target); if (self.targets[lc] === undefined) self.targets[lc] = new Array(); self.sidebar.addChannelView(target, true); }, "/part": function(name) { if (name === undefined) { if (!self.activeTarget.startsWith("#")) return; name = self.activeTarget; } self.connection.call("PART", name); }, "/msg": function(target, ...message) { self.sendMessageToTarget(target, message.join(" ")); }, "/help": function() { self.addItem(self.activeTarget, self.systemNick, `Available commands: /msg [nick or channel] message /nick [nickname] - change your nickname, e.g. /nick Grandmaster /join [channel] - join a channel, e.g. /join #ZeeZide /part [channel]? - leave a channel, e.g. current /leave `, false, true); } }; self.messageMap = { "NOTICE": function(message) { self.submit(this.systemNick, message.arguments[1] || "?", true); }, "NICK": function(message) { if (message.arguments.length == 0) return; const name = message.arguments[0]; self.nick = name; self.notice(`Changed nick to: ${name}`) self.sidebar.setNick(name); }, "JOIN": function(message) { if (message.arguments.length < 1) return; const channel = message.arguments[0]; if (message.origin === self.nick) { const lc = channel.toLowerCase(); if (self.targets[lc] === undefined) self.targets[lc] = new Array(); self.sidebar.addChannelView(channel, false); } else { self.addItem(channel, message.origin, `${message.origin} joined ${channel}.`, true); } }, "PART": function(message) { if (message.arguments.length < 1) return; const channel = message.arguments[0]; if (message.origin === self.nick) self.sidebar.removeChannelView(channel); else { self.addItem(channel, message.origin, `${message.origin} left ${channel}.`, true, true); } }, "PRIVMSG": function(message) { if (message.arguments.length < 2) return; const target = message.arguments[0]; const text = message.arguments[1]; const sender = message.origin || "???"; if (target.toLowerCase() === self.nick.toLowerCase()) { if (self.targets[sender.toLowerCase()] === undefined) self.sidebar.addQueryView(sender, true); self.addItem(sender, sender, text); } else self.addItem(target, sender, text); }, "Z-CHANNEL-TOPIC": function(message) { // TODO: set topic of a channel. } }; // Add a chat message to the target arrays, and if the target is visible, // to the list of chats. If not visible, update the unread count. self.addItem = function(target, author, message, isSystem, isRead) { const lc = target.toLowerCase(); const item = new ChatItem(message, author, new Date(), isSystem, isRead); if (self.targets[lc] === undefined) self.targets[lc] = new Array(); self.targets[lc].push(item); if (self.activeTarget === lc) { self.messages.addItem(item); } else { let unreadCount = 0; self.targets[lc].forEach(function(item) { if (!item.isRead) unreadCount++; }); self.sidebar.updateUnreadCount(target, unreadCount); } } self.submit = function(author, message, isSystem ) { // DEPRECATED self.addItem(self.serverTarget, author, message, isSystem || false) }; self.notice = function(message) { // DEPRECATED self.addItem(self.serverTarget, this.systemNick, message, true) }; // Called by the SidebarVC when a new target is clicked. self.onTargetChange = function(newTarget) { const lc = newTarget.toLowerCase() if (self.activeTarget === lc) return; self.activeTarget = lc; self.messages.setTitle(newTarget); self.messages.setChatItems(self.targets[lc]); self.sidebar.updateUnreadCount(newTarget, 0); }; // Called by the MessagesVC if the user enters a command line. self.onLine = function(line) { if (line.startsWith("/")) { const pair = line.split(" "); const cmd = pair[0].toLowerCase(); if (self.commandMap[cmd] !== undefined) { // TODO: slice&apply self.commandMap[cmd](pair[1], pair[2], pair[3], pair[4], pair[5]); } else { // TBD: we could still send this to the server! self.addItem(self.serverTarget, this.systemNick, `unknown command: ${cmd}`, true); } return; } if (self.activeTarget === self.serverTarget) { self.addItem(self.serverTarget, this.systemNick, "Cannot send message to server itself, /help for help!", true); } else { self.sendMessageToTarget(self.activeTarget, line); } }; self.sendMessageToTarget = function(target, message) { if (self.targets[target.toLowerCase()] === undefined) { if (target.startsWith("#")) return self.notice("You did not join this channel yet!"); self.sidebar.addQueryView(target, true); } self.connection.call("PRIVMSG", target || self.activeTarget, message); self.addItem(target || self.activeTarget, self.nick, message, false, true); }; // This is called when we receive a new IRC message from the bridge. self.onMessage = function(message) { if (self.messageMap[message.command] !== undefined) self.messageMap[message.command](message); else self.submit(self.systemNick, `unknown message: ${message.command}`, true); }; self.connection = new Connection( webSockEndPointURL || "ws://localhost:1337/websocket", self.onMessage ); self.connect = function() { self.notice("Connecting to IRC bridge ..."); self.connection.connect(function() { {{script.vc.MainVC.onConnect}} }); }; self.viewDidAppear = function() { const self = this; self.messages.viewDidAppear(); {{script.app.onStartup}} self.connect(); }; } """ // Generated from ViewControllers/MessagesVC.js // on Fri Jun 8 16:03:12 CEST 2018 // let rsrc_MessagesVC_js = """ //===----------------------------------------------------------------------===// // // This source file is part of the swift-nio-irc open source project // // Copyright (c) 2018 ZeeZide GmbH. and the swift-nio-irc project authors // Licensed under Apache License v2.0 // // See LICENSE.txt for license information // See CONTRIBUTORS.txt for the list of SwiftNIOIRC project authors // // SPDX-License-Identifier: Apache-2.0 // //===----------------------------------------------------------------------===// const MessagesVC = function(onLine) { const self = this; self.onLine = onLine; self.loadView = function(parent) { self.view = parent.querySelector("main"); self.form = self.view.querySelector("footer form"); self.messageListView = self.view.querySelector("section"); }; self.viewDidAppear = function() { const self = this; self.form.querySelector("input").focus(); self.form.addEventListener("submit", function(e) { e.preventDefault() self.onFormSubmit(); } ); }; self.setTitle = function(newTitle) { self.view.querySelector("#chatTitle").innerText = newTitle; }; self.clear = function() { while (self.messageListView.firstChild) { self.messageListView.removeChild(self.messageListView.firstChild); } }; /// Clear the whole history and set the new items. self.setChatItems = function(newItems) { self.clear(); if (newItems === undefined) return; newItems.forEach(function(item) { self.addItem(item); }); }; self.onFormSubmit = function() { const field = self.form.querySelector("input"); const line = field.value; field.value = ""; if (self.onLine !== undefined) { self.onLine(line) } }; self.addItem = function(item) { const self = this; const escapedAuthor = htmlEscape(item.sender); const escapedMessage = htmlEscape(item.message); const formattedDate = formatDate(item.time); const timestamp = item.time.toString(); const newArticle = document.createElement("article") newArticle.innerHTML = `
${escapedMessage}