PageRenderTime 105ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/client/client.js

https://gitlab.com/varunkothamachu/hack.chat
JavaScript | 282 lines | 235 code | 44 blank | 3 comment | 31 complexity | cd9daf000f33dc40e3f2312539974c5c MD5 | raw file
  1. var motd = [
  2. " _ _ _ _ ",
  3. " | |_ ___ ___| |_ ___| |_ ___| |_ ",
  4. " | |_ || _| '_| | _| |_ || _|",
  5. " |_|_|__/|___|_,_|.|___|_|_|__/|_| ",
  6. "",
  7. "",
  8. "Welcome to hack.chat, a minimal, distraction-free chat application.",
  9. "Channels are created and joined by going to https://hack.chat/?your-channel. There are no channel lists, so a secret channel name can be used for private discussions.",
  10. "",
  11. "Here are some pre-made channels you can join:",
  12. "?lobby ?meta ?random",
  13. "?technology ?programming",
  14. "?math ?physics ?asciiart",
  15. "And here's a random one generated just for you: ?" + Math.random().toString(36).substr(2, 8),
  16. "",
  17. "",
  18. "",
  19. "Formatting:",
  20. "Whitespace is preserved, so source code can be shared properly.",
  21. "Surround LaTeX with $ for inline style $\\zeta(2) = \\pi^2/6$, or $$ for display.",
  22. "$$\\int_0^1 \\int_0^1 \\frac{1}{1-xy} dx dy = \\frac{\\pi^2}{6}$$",
  23. "",
  24. "",
  25. "Vortico is the one and only admin. All others claiming to be are imposters.",
  26. "",
  27. "GitHub repo: https://github.com/AndrewBelt/hack.chat",
  28. "Server and client released under the GNU General Public License.",
  29. "No message history is retained on the hack.chat server.",
  30. ].join("\n")
  31. function $(query) {return document.querySelector(query)}
  32. window.onload = function() {
  33. loadScheme()
  34. var channel = window.location.search.replace(/^\?/, '')
  35. if (channel == '') {
  36. pushMessage('', motd)
  37. }
  38. else {
  39. join(channel)
  40. }
  41. }
  42. var ws
  43. var myNick
  44. function join(channel) {
  45. ws = new WebSocket('ws://' + document.domain + ':6060')
  46. // ws = new WebSocket('wss://' + document.domain + '/chat-ws')
  47. ws.onopen = function() {
  48. myNick = prompt('Nickname:')
  49. if (myNick) {
  50. ws.send(JSON.stringify({cmd: 'join', channel: channel, nick: myNick}))
  51. }
  52. }
  53. ws.onmessage = function(message) {
  54. var args = JSON.parse(message.data)
  55. var cmd = args.cmd
  56. var command = COMMANDS[cmd]
  57. command.call(null, args)
  58. }
  59. ws.onclose = function() {
  60. pushMessage('!', "Server disconnected", Date.now(), 'warn')
  61. }
  62. // prepare footer
  63. $('#footer').classList.remove('hidden')
  64. $('#footer').onclick = function() {
  65. $('#chatinput').focus()
  66. }
  67. $('#chatinput').onkeydown = function(e) {
  68. if (e.keyCode == 13 && !e.shiftKey) {
  69. if ($('#chatinput').value != '') {
  70. ws.send(JSON.stringify({cmd: 'chat', text: $('#chatinput').value}))
  71. $('#chatinput').value = ''
  72. updateInputSize()
  73. }
  74. e.preventDefault()
  75. }
  76. }
  77. $('#chatinput').focus()
  78. $('#chatinput').addEventListener('input', function() {
  79. updateInputSize()
  80. })
  81. updateInputSize()
  82. // prepare sidebar
  83. $('#sidebar').onmouseenter = function() {
  84. $('#sidebar-content').classList.remove('hidden')
  85. }
  86. $('#sidebar').onmouseleave = function() {
  87. $('#sidebar-content').classList.add('hidden')
  88. }
  89. }
  90. var COMMANDS = {
  91. chat: function(args) {
  92. var cls
  93. if (args.admin) {
  94. cls = 'admin'
  95. }
  96. else if (myNick == args.nick) {
  97. cls = 'me'
  98. }
  99. pushMessage(args.nick, args.text, args.time, cls)
  100. },
  101. info: function(args) {
  102. pushMessage('*', args.text, args.time, 'info')
  103. },
  104. warn: function(args) {
  105. pushMessage('!', args.text, args.time, 'warn')
  106. },
  107. onlineSet: function(args) {
  108. var nicks = args.nicks
  109. nicks.sort()
  110. for (var i = 0; i < nicks.length; i++) {
  111. users[nicks[i]] = true
  112. }
  113. updateUsers()
  114. pushMessage('*', "Users online: " + nicks.join(", "), Date.now(), 'info')
  115. },
  116. onlineAdd: function(args) {
  117. var nick = args.nick
  118. users[nick] = true
  119. updateUsers()
  120. if (nick != myNick) {
  121. pushMessage('*', nick + " joined", Date.now(), 'info')
  122. }
  123. },
  124. onlineRemove: function(args) {
  125. var nick = args.nick
  126. delete users[nick]
  127. updateUsers()
  128. pushMessage('*', nick + " left", Date.now(), 'info')
  129. },
  130. }
  131. function updateInputSize() {
  132. var atBottom = isAtBottom()
  133. var input = $('#chatinput')
  134. input.style.height = 0
  135. input.style.height = input.scrollHeight + 'px'
  136. document.body.style.marginBottom = $('#footer').offsetHeight + 'px'
  137. if (atBottom) {
  138. window.scrollTo(0, document.body.scrollHeight)
  139. }
  140. }
  141. function pushMessage(nick, text, time, cls) {
  142. var messageEl = document.createElement('div')
  143. messageEl.classList.add('message')
  144. if (cls) {
  145. messageEl.classList.add(cls)
  146. }
  147. var nickEl = document.createElement('span')
  148. nickEl.classList.add('nick')
  149. nickEl.textContent = nick || ''
  150. if (time) {
  151. var date = new Date(time)
  152. nickEl.title = date.toLocaleString()
  153. }
  154. messageEl.appendChild(nickEl)
  155. var textEl = document.createElement('pre')
  156. textEl.classList.add('text')
  157. textEl.textContent = text || ''
  158. textEl.innerHTML = textEl.innerHTML.replace(/(\s|^)((\?|https?:\/\/)\S+?)(?=[,.!?:)]?\s|$)/g, parseLinks)
  159. try {
  160. renderMathInElement(textEl, {delimiters: [
  161. {left: "$$", right: "$$", display: true},
  162. {left: "$", right: "$", display: false},
  163. ]})
  164. }
  165. catch (e) {
  166. console.warn(e)
  167. }
  168. messageEl.appendChild(textEl)
  169. var atBottom = isAtBottom()
  170. $('#messages').appendChild(messageEl)
  171. if (atBottom) {
  172. window.scrollTo(0, document.body.scrollHeight)
  173. }
  174. unread += 1
  175. updateTitle()
  176. }
  177. function send(data) {
  178. ws.send(JSON.stringify(data))
  179. }
  180. function parseLinks(g0, g1, g2) {
  181. var a = document.createElement('a')
  182. a.innerHTML = g0
  183. var url = a.textContent
  184. if (url[0] == '?') {
  185. url = "/" + url
  186. }
  187. a.href = url
  188. a.target = '_blank'
  189. return g1 + a.outerHTML
  190. }
  191. var windowActive = true
  192. var unread = 0
  193. window.onfocus = function() {
  194. windowActive = true
  195. updateTitle()
  196. }
  197. window.onblur = function() {
  198. windowActive = false
  199. }
  200. window.onscroll = function() {
  201. if (isAtBottom()) {
  202. updateTitle()
  203. }
  204. }
  205. function isAtBottom() {
  206. return (window.innerHeight + window.scrollY) >= document.body.scrollHeight
  207. }
  208. function updateTitle() {
  209. if (windowActive && isAtBottom()) {
  210. unread = 0
  211. }
  212. var title = ''
  213. if (unread > 0) {
  214. title += '(' + unread + ') '
  215. }
  216. title += 'hack.chat'
  217. document.title = title
  218. }
  219. var users = {}
  220. function updateUsers() {
  221. var usersArr = Object.keys(users)
  222. usersArr.sort()
  223. $('#users').textContent = usersArr.join("\n")
  224. }
  225. function setScheme(name) {
  226. $('#scheme-link').href = "/schemes/" + name + ".css"
  227. if (localStorage) {
  228. localStorage['scheme'] = name
  229. }
  230. }
  231. function loadScheme() {
  232. if (localStorage) {
  233. var name = localStorage['scheme']
  234. if (name) {
  235. setScheme(name)
  236. }
  237. }
  238. }