Guest User

Untitled

a guest
Aug 4th, 2017
229
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. var frontpage = [
  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.     "?lounge ?meta",
  13.     "?math ?physics ?chemistry",
  14.     "?technology ?programming",
  15.     "?games ?banana",
  16.     "And here's a random one generated just for you: ?" + Math.random().toString(36).substr(2, 8),
  17.     "",
  18.     "",
  19.     "Formatting:",
  20.     "Whitespace is preserved, so source code can be pasted verbatim.",
  21.     "Surround LaTeX with a dollar sign for inline style $\\zeta(2) = \\pi^2/6$, and two dollars for display. $$\\int_0^1 \\int_0^1 \\frac{1}{1-xy} dx dy = \\frac{\\pi^2}{6}$$",
  22.     "",
  23.     "GitHub: https://github.com/AndrewBelt/hack.chat",
  24.     "Android apps: https://goo.gl/UkbKYy https://goo.gl/qasdSu",
  25.     "",
  26.     "Server and web client released under the MIT open source license.",
  27.     "No message history is retained on the hack.chat server.",
  28. ].join("\n")
  29.  
  30. function $(query) {return document.querySelector(query)}
  31.  
  32. function localStorageGet(key) {
  33.     try {
  34.         return window.localStorage[key]
  35.     }
  36.     catch(e) {}
  37. }
  38.  
  39. function localStorageSet(key, val) {
  40.     try {
  41.         window.localStorage[key] = val
  42.     }
  43.     catch(e) {}
  44. }
  45.  
  46.  
  47. var ws
  48. var myNick = localStorageGet('my-nick')
  49. var myChannel = window.location.search.replace(/^\?/, '')
  50. var lastSent = [""]
  51. var lastSentPos = 0
  52.  
  53.  
  54. // Ping server every 50 seconds to retain WebSocket connection
  55. window.setInterval(function() {
  56.     send({cmd: 'ping'})
  57. }, 50000)
  58.  
  59.  
  60. function join(channel) {
  61.     if (document.domain == 'hack.chat') {
  62.         // For https://hack.chat/
  63.         ws = new WebSocket('wss://hack.chat/chat-ws')
  64.     }
  65.     else {
  66.         // for local installs
  67.         ws = new WebSocket('ws://' + document.domain + ':6060')
  68.     }
  69.  
  70.     var wasConnected = false
  71.  
  72.     ws.onopen = function() {
  73.         if (!wasConnected) {
  74.             if (location.hash) {
  75.                 myNick = location.hash.substr(1)
  76.             }
  77.             else {
  78.                 myNick = prompt('Nickname:', myNick)
  79.             }
  80.         }
  81.         if (myNick) {
  82.             localStorageSet('my-nick', myNick)
  83.             send({cmd: 'join', channel: channel, nick: myNick})
  84.         }
  85.         wasConnected = true
  86.     }
  87.  
  88.     ws.onclose = function() {
  89.         if (wasConnected) {
  90.             pushMessage({nick: '!', text: "Server disconnected. Attempting to reconnect..."})
  91.         }
  92.         window.setTimeout(function() {
  93.             join(channel)
  94.         }, 2000)
  95.     }
  96.  
  97.     ws.onmessage = function(message) {
  98.         var args = JSON.parse(message.data)
  99.         var cmd = args.cmd
  100.         var command = COMMANDS[cmd]
  101.         command.call(null, args)
  102.     }
  103. }
  104.  
  105.  
  106. var COMMANDS = {
  107.     chat: function(args) {
  108.         if (ignoredUsers.indexOf(args.nick) >= 0) {
  109.             return
  110.         }
  111.         pushMessage(args)
  112.     },
  113.     info: function(args) {
  114.         args.nick = '*'
  115.         pushMessage(args)
  116.     },
  117.     warn: function(args) {
  118.         args.nick = '!'
  119.         pushMessage(args)
  120.     },
  121.     onlineSet: function(args) {
  122.         var nicks = args.nicks
  123.         usersClear()
  124.         nicks.forEach(function(nick) {
  125.             userAdd(nick)
  126.         })
  127.         pushMessage({nick: '*', text: "Users online: " + nicks.join(", ")})
  128.     },
  129.     onlineAdd: function(args) {
  130.         var nick = args.nick
  131.         userAdd(nick)
  132.         if ($('#joined-left').checked) {
  133.             pushMessage({nick: '*', text: nick + " joined"})
  134.         }
  135.     },
  136.     onlineRemove: function(args) {
  137.         var nick = args.nick
  138.         userRemove(nick)
  139.         if ($('#joined-left').checked) {
  140.             pushMessage({nick: '*', text: nick + " left"})
  141.         }
  142.     },
  143. }
  144.  
  145.  
  146. function pushMessage(args) {
  147.     // Message container
  148.     var messageEl = document.createElement('div')
  149.     messageEl.classList.add('message')
  150.  
  151.     if (args.nick == myNick) {
  152.         messageEl.classList.add('me')
  153.     }
  154.     else if (args.nick == '!') {
  155.         messageEl.classList.add('warn')
  156.     }
  157.     else if (args.nick == '*') {
  158.         messageEl.classList.add('info')
  159.     }
  160.     else if (args.admin) {
  161.         messageEl.classList.add('admin')
  162.     }
  163.     else if (args.mod) {
  164.         messageEl.classList.add('mod')
  165.     }
  166.  
  167.     // Nickname
  168.     var nickSpanEl = document.createElement('span')
  169.     nickSpanEl.classList.add('nick')
  170.     messageEl.appendChild(nickSpanEl)
  171.  
  172.     if (args.trip) {
  173.         var tripEl = document.createElement('span')
  174.         tripEl.textContent = args.trip + " "
  175.         tripEl.classList.add('trip')
  176.         nickSpanEl.appendChild(tripEl)
  177.     }
  178.  
  179.     if (args.nick) {
  180.         var nickLinkEl = document.createElement('a')
  181.         nickLinkEl.textContent = args.nick
  182.         nickLinkEl.onclick = function() {
  183.             insertAtCursor("@" + args.nick + " ")
  184.             $('#chatinput').focus()
  185.         }
  186.         var date = new Date(args.time || Date.now())
  187.         nickLinkEl.title = date.toLocaleString()
  188.         nickSpanEl.appendChild(nickLinkEl)
  189.     }
  190.  
  191.     // Text
  192.     var textEl = document.createElement('pre')
  193.     textEl.classList.add('text')
  194.  
  195.     textEl.textContent = args.text || ''
  196.     textEl.innerHTML = textEl.innerHTML.replace(/(\?|https?:\/\/)\S+?(?=[,.!?:)]?\s|$)/g, parseLinks)
  197.  
  198.     if ($('#parse-latex').checked) {
  199.         // Temporary hotfix for \rule spamming, see https://github.com/Khan/KaTeX/issues/109
  200.         textEl.innerHTML = textEl.innerHTML.replace(/\\rule|\\\\\s*\[.*?\]/g, '')
  201.         try {
  202.             renderMathInElement(textEl, {delimiters: [
  203.                 {left: "$$", right: "$$", display: true},
  204.                 {left: "$", right: "$", display: false},
  205.             ]})
  206.         }
  207.         catch (e) {
  208.             console.warn(e)
  209.         }
  210.     }
  211.  
  212.     messageEl.appendChild(textEl)
  213.  
  214.     // Scroll to bottom
  215.     var atBottom = isAtBottom()
  216.     $('#messages').appendChild(messageEl)
  217.     if (atBottom) {
  218.         window.scrollTo(0, document.body.scrollHeight)
  219.     }
  220.  
  221.     unread += 1
  222.     updateTitle()
  223. }
  224.  
  225.  
  226. function insertAtCursor(text) {
  227.     var input = $('#chatinput')
  228.     var start = input.selectionStart || 0
  229.     var before = input.value.substr(0, start)
  230.     var after = input.value.substr(start)
  231.     before += text
  232.     input.value = before + after
  233.     input.selectionStart = input.selectionEnd = before.length
  234.     updateInputSize()
  235. }
  236.  
  237.  
  238. function send(data) {
  239.     if (ws && ws.readyState == ws.OPEN) {
  240.         ws.send(JSON.stringify(data))
  241.     }
  242. }
  243.  
  244.  
  245. function parseLinks(g0) {
  246.     var a = document.createElement('a')
  247.     a.innerHTML = g0
  248.     var url = a.textContent
  249.     a.href = url
  250.     a.target = '_blank'
  251.     return a.outerHTML
  252. }
  253.  
  254.  
  255. var windowActive = true
  256. var unread = 0
  257.  
  258. window.onfocus = function() {
  259.     windowActive = true
  260.     updateTitle()
  261. }
  262.  
  263. window.onblur = function() {
  264.     windowActive = false
  265. }
  266.  
  267. window.onscroll = function() {
  268.     if (isAtBottom()) {
  269.         updateTitle()
  270.     }
  271. }
  272.  
  273. function isAtBottom() {
  274.     return (window.innerHeight + window.scrollY) >= (document.body.scrollHeight - 1)
  275. }
  276.  
  277. function updateTitle() {
  278.     if (windowActive && isAtBottom()) {
  279.         unread = 0
  280.     }
  281.  
  282.     var title
  283.     if (myChannel) {
  284.         title = "?" + myChannel
  285.     }
  286.     else {
  287.         title = "hack.chat"
  288.     }
  289.     if (unread > 0) {
  290.         title = '(' + unread + ') ' + title
  291.     }
  292.     document.title = title
  293. }
  294.  
  295. /* footer */
  296.  
  297. $('#footer').onclick = function() {
  298.     $('#chatinput').focus()
  299. }
  300.  
  301. $('#chatinput').onkeydown = function(e) {
  302.     if (e.keyCode == 13 /* ENTER */ && !e.shiftKey) {
  303.         e.preventDefault()
  304.         // Submit message
  305.         if (e.target.value != '') {
  306.             var text = e.target.value
  307.             e.target.value = ''
  308.             send({cmd: 'chat', text: text})
  309.             lastSent[0] = text
  310.             lastSent.unshift("")
  311.             lastSentPos = 0
  312.             updateInputSize()
  313.         }
  314.     }
  315.     else if (e.keyCode == 38 /* UP */) {
  316.         // Restore previous sent messages
  317.         if (e.target.selectionStart === 0 && lastSentPos < lastSent.length - 1) {
  318.             e.preventDefault()
  319.             if (lastSentPos == 0) {
  320.                 lastSent[0] = e.target.value
  321.             }
  322.             lastSentPos += 1
  323.             e.target.value = lastSent[lastSentPos]
  324.             e.target.selectionStart = e.target.selectionEnd = e.target.value.length
  325.             updateInputSize()
  326.         }
  327.     }
  328.     else if (e.keyCode == 40 /* DOWN */) {
  329.         if (e.target.selectionStart === e.target.value.length && lastSentPos > 0) {
  330.             e.preventDefault()
  331.             lastSentPos -= 1
  332.             e.target.value = lastSent[lastSentPos]
  333.             e.target.selectionStart = e.target.selectionEnd = 0
  334.             updateInputSize()
  335.         }
  336.     }
  337.     else if (e.keyCode == 27 /* ESC */) {
  338.         e.preventDefault()
  339.         // Clear input field
  340.         e.target.value = ""
  341.         lastSentPos = 0
  342.         lastSent[lastSentPos] = ""
  343.         updateInputSize()
  344.     }
  345.     else if (e.keyCode == 9 /* TAB */) {
  346.         // Tab complete nicknames starting with @
  347.         e.preventDefault()
  348.         var pos = e.target.selectionStart || 0
  349.         var text = e.target.value
  350.         var index = text.lastIndexOf('@', pos)
  351.         if (index >= 0) {
  352.             var stub = text.substring(index + 1, pos).toLowerCase()
  353.             // Search for nick beginning with stub
  354.             var nicks = onlineUsers.filter(function(nick) {
  355.                 return nick.toLowerCase().indexOf(stub) == 0
  356.             })
  357.             if (nicks.length == 1) {
  358.                 insertAtCursor(nicks[0].substr(stub.length) + " ")
  359.             }
  360.         }
  361.     }
  362. }
  363.  
  364.  
  365. function updateInputSize() {
  366.     var atBottom = isAtBottom()
  367.  
  368.     var input = $('#chatinput')
  369.     input.style.height = 0
  370.     input.style.height = input.scrollHeight + 'px'
  371.     document.body.style.marginBottom = $('#footer').offsetHeight + 'px'
  372.  
  373.     if (atBottom) {
  374.         window.scrollTo(0, document.body.scrollHeight)
  375.     }
  376. }
  377.  
  378. $('#chatinput').oninput = function() {
  379.     updateInputSize()
  380. }
  381.  
  382. updateInputSize()
  383.  
  384.  
  385. /* sidebar */
  386.  
  387. $('#sidebar').onmouseenter = $('#sidebar').ontouchstart = function(e) {
  388.     $('#sidebar-content').classList.remove('hidden')
  389.     e.stopPropagation()
  390. }
  391.  
  392. $('#sidebar').onmouseleave = document.ontouchstart = function() {
  393.     if (!$('#pin-sidebar').checked) {
  394.         $('#sidebar-content').classList.add('hidden')
  395.     }
  396. }
  397.  
  398. $('#clear-messages').onclick = function() {
  399.     // Delete children elements
  400.     var messages = $('#messages')
  401.     while (messages.firstChild) {
  402.         messages.removeChild(messages.firstChild)
  403.     }
  404. }
  405.  
  406. // Restore settings from localStorage
  407.  
  408. if (localStorageGet('pin-sidebar') == 'true') {
  409.     $('#pin-sidebar').checked = true
  410.     $('#sidebar-content').classList.remove('hidden')
  411. }
  412. if (localStorageGet('joined-left') == 'false') {
  413.     $('#joined-left').checked = false
  414. }
  415. if (localStorageGet('parse-latex') == 'false') {
  416.     $('#parse-latex').checked = false
  417. }
  418.  
  419. $('#pin-sidebar').onchange = function(e) {
  420.     localStorageSet('pin-sidebar', !!e.target.checked)
  421. }
  422. $('#joined-left').onchange = function(e) {
  423.     localStorageSet('joined-left', !!e.target.checked)
  424. }
  425. $('#parse-latex').onchange = function(e) {
  426.     localStorageSet('parse-latex', !!e.target.checked)
  427. }
  428.  
  429. // User list
  430.  
  431. var onlineUsers = []
  432. var ignoredUsers = []
  433.  
  434. function userAdd(nick) {
  435.     var user = document.createElement('a')
  436.     user.textContent = nick
  437.     user.onclick = function(e) {
  438.         userInvite(nick)
  439.     }
  440.     var userLi = document.createElement('li')
  441.     userLi.appendChild(user)
  442.     $('#users').appendChild(userLi)
  443.     onlineUsers.push(nick)
  444. }
  445.  
  446. function userRemove(nick) {
  447.     var users = $('#users')
  448.     var children = users.children
  449.     for (var i = 0; i < children.length; i++) {
  450.         var user = children[i]
  451.         if (user.textContent == nick) {
  452.             users.removeChild(user)
  453.         }
  454.     }
  455.     var index = onlineUsers.indexOf(nick)
  456.     if (index >= 0) {
  457.         onlineUsers.splice(index, 1)
  458.     }
  459. }
  460.  
  461. function usersClear() {
  462.     var users = $('#users')
  463.     while (users.firstChild) {
  464.         users.removeChild(users.firstChild)
  465.     }
  466.     onlineUsers.length = 0
  467. }
  468.  
  469. function userInvite(nick) {
  470.     send({cmd: 'invite', nick: nick})
  471. }
  472.  
  473. function userIgnore(nick) {
  474.     ignoredUsers.push(nick)
  475. }
  476.  
  477. /* color scheme switcher */
  478.  
  479. var schemes = [
  480.     'android',
  481.     'atelier-dune',
  482.     'atelier-forest',
  483.     'atelier-heath',
  484.     'atelier-lakeside',
  485.     'atelier-seaside',
  486.     'bright',
  487.     'chalk',
  488.     'default',
  489.     'eighties',
  490.     'greenscreen',
  491.     'mocha',
  492.     'monokai',
  493.     'nese',
  494.     'ocean',
  495.     'pop',
  496.     'railscasts',
  497.     'solarized',
  498.     'tomorrow',
  499. ]
  500.  
  501. var currentScheme = 'atelier-dune'
  502.  
  503. function setScheme(scheme) {
  504.     currentScheme = scheme
  505.     $('#scheme-link').href = "/schemes/" + scheme + ".css"
  506.     localStorageSet('scheme', scheme)
  507. }
  508.  
  509. // Add scheme options to dropdown selector
  510. schemes.forEach(function(scheme) {
  511.     var option = document.createElement('option')
  512.     option.textContent = scheme
  513.     option.value = scheme
  514.     $('#scheme-selector').appendChild(option)
  515. })
  516.  
  517. $('#scheme-selector').onchange = function(e) {
  518.     setScheme(e.target.value)
  519. }
  520.  
  521. // Load sidebar configaration values from local storage if available
  522. if (localStorageGet('scheme')) {
  523.     setScheme(localStorageGet('scheme'))
  524. }
  525.  
  526. $('#scheme-selector').value = currentScheme
  527.  
  528.  
  529. /* main */
  530.  
  531. if (myChannel == '') {
  532.     pushMessage({text: frontpage})
  533.     $('#footer').classList.add('hidden')
  534.     $('#sidebar').classList.add('hidden')
  535. }
  536. else {
  537.     join(myChannel)
  538. }
Advertisement
Add Comment
Please, Sign In to add comment