commit 86652e790c7de5dd3a42266b3c351116b7b1d224 Author: mappu Date: Sat Oct 8 14:58:37 2016 +1300 initial commit diff --git a/.hgignore b/.hgignore new file mode 100644 index 0000000..d58532a --- /dev/null +++ b/.hgignore @@ -0,0 +1,6 @@ +mode:regex + +\.exe$ + +^_dist/ +^bindata\.go$ \ No newline at end of file diff --git a/Config.go b/Config.go new file mode 100644 index 0000000..d6c8d07 --- /dev/null +++ b/Config.go @@ -0,0 +1,23 @@ +package main + +type Config struct { + App struct { + Name string + Version string + MotdHTML string + Debug bool + } + + Web struct { + Port int + BindTo string + Extern string + Title string + } + + Hub struct { + Address string + Port int + Tag string + } +} diff --git a/build.sh b/build.sh new file mode 100644 index 0000000..6cffd23 --- /dev/null +++ b/build.sh @@ -0,0 +1,167 @@ +#!/bin/bash +# godist.sh is a template build script for golang applications. +# +# Copyright (c) 2016, The godist.sh Author(s) +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +# AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +# PERFORMANCE OF THIS SOFTWARE. +# + +set -eu + +DIST_DIR=./_dist + +get_package_name() { + # Binaries are named after the containing folder. + pwd | tr / $'\n' | tail -n1 +} + +check_dependencies() { + # `set -e` will take care of killing the application if the dependency + # goes unresolved. + which 7z > /dev/null + which php > /dev/null +} + +get_platform_description() { + if [[ $GOOS == windows ]] ; then + echo -n "win" + else + echo -n "${GOOS}" + fi + + if [[ $GOARCH == 386 ]] ; then + echo -n 32 + elif [[ $GOARCH == amd64 ]] ; then + echo -n 64 + else + echo -n "-${GOARCH}" + if [[ -v $GOARM ]] ; then + echo -n "v${GOARM}" + fi + fi +} + +pathfix() { + if [[ $(uname -o) == Cygwin ]] ; then + cygpath -w "$1" + else + echo "$1" + fi +} + +single_build() { + local version="$1" + + # Determine build output name + local bin_name='' + if [[ $GOOS == windows ]] ; then + local_bin_name="$(get_package_name).exe" + else + local_bin_name="$(get_package_name)" + fi + + if [[ -f ./$local_bin_name ]] ; then + rm -f "./$local_bin_name" + fi + + local tmpdir=$(mktemp -d) + + # Platform identifier + local platform="$(get_platform_description)" + echo "[INFO] Building ${version} for ${platform}..." + + # Build. + # GOARCH/GOOS supplied in function env + go build \ + -a \ + -ldflags '-s -w' \ + -gcflags "-trimpath=${GOPATH}" \ + -asmflags "-trimpath=${GOPATH}" \ + -o "$(pathfix "${tmpdir}/${local_bin_name}")" + + # Archive. + if [[ ! -d $DIST_DIR ]] ; then + mkdir "$DIST_DIR" + fi + local archive_name="${DIST_DIR}/$(get_package_name)-${version}-${platform}" + if [[ $GOOS == windows ]] ; then + archive_name="${archive_name}.7z" + 7z a -mx9 "$archive_name" "${tmpdir}/${local_bin_name}" >/dev/null + else + archive_name="${archive_name}.tar.xz" + XZ_OPT='-9' tar caf "$archive_name" -C "${tmpdir}" "${local_bin_name}" --owner=0 --group=0 >/dev/null + fi + + # Cleanup + rm -f "${tmpdir}/${local_bin_name}" + rmdir "$tmpdir" + +} + +datestamp() { + ( TZ=UTC date +%Y%m%d%H%M%SZ ) +} + +usage() { + cat <&2 + exit 1 + fi + if [[ ! -d $GOPATH ]] ; then + echo "Invalid GOPATH set." >&2 + exit 1 + fi + + local version="" + while getopts ':v:' flag ; do + case "$flag" in + v) + version="$OPTARG" + ;; + *) + usage + ;; + esac + done + + if [[ $version == "" ]] ; then + read -p "Enter version string (blank for timestamp)> " version + if [[ $version == "" ]] ; then + version=$(datestamp) + fi + fi + + if [[ -f ./bindata.go ]] ; then + rm ./bindata.go + fi + go-bindata -nomemcopy -prefix client client + + GOARCH=amd64 GOOS=windows single_build "$version" + GOARCH=386 GOOS=windows single_build "$version" + GOARCH=amd64 GOOS=linux single_build "$version" + GOARCH=386 GOOS=linux single_build "$version" + + echo "[INFO] Build complete." +} + +main "$@" diff --git a/client/apple-touch-icon.png b/client/apple-touch-icon.png new file mode 100644 index 0000000..7746f2e Binary files /dev/null and b/client/apple-touch-icon.png differ diff --git a/client/dcwebui.css b/client/dcwebui.css new file mode 100644 index 0000000..45e7291 --- /dev/null +++ b/client/dcwebui.css @@ -0,0 +1,396 @@ +/* dcwebui.css */ + +* { + -webkit-text-size-adjust:100% + -moz-text-size-adjust:100% + -ms-text-size-adjust:100% +} + +html,body { + height:100%; + width:100%; + margin:0; + padding:0; + border:0; + font-family:Geneva,Helvetica,Arial,sans-serif; + font-size: 12px; +} +.placement-top { + height:24px; + + position:absolute; + top:0; + left:0; + right:0; +} +.placement-mid { + position:absolute; + top:24px; + right:0; + bottom:40px; + left:0; +} +.placement-bottom { + height:40px; + + position:absolute; + bottom:0; + left:0; + right:0; +} +.placement-panel { + width:150px; + + position:absolute; + right:0; + top:24px; + bottom:40px; +} +.placement-vary { + /* Nothing, at this media query level */ +} + +/* WiiU placements */ +.navigator-wiiu .placement-mid { + bottom:120px; +} +.navigator-wiiu .placement-bottom { + height:120px; +} +.navigator-wiiu .placement-panel { + bottom:120px; +} + +/* Menu button */ +.menubutton { + position:absolute; + right:2px; + top:2px; + + cursor:pointer; + color:black; + font-weight:bold; + + line-height:18px; + width:18px; + text-align:center; +} +.menubutton:hover { + background:white; +} +/* Menu list */ +.menu { + background:#DDD; + + box-shadow: 0 4px 24px #CCC; + + display:none; /* default state */ + + position:absolute; /* but not positioned anywhere specifically */ + + width:200px; + z-index:10; /* above placement-panel */ + + overflow-y:auto; +} +.menu ul { + margin:0; + padding:0; +} +.menu li { + list-style-type:none; + padding:6px; + cursor:pointer; +} +.menu li:hover { + background:#333; + color:white; +} +/* Tabs */ +.tabbar { + position:relative; + overflow:hidden; + + background:darkgrey; + background-image: linear-gradient(bottom,#BBBBBB,#EEEEEE); + background-image: -ms-linear-gradient(bottom,#BBBBBB,#EEEEEE); + background-image: -moz-linear-gradient(bottom,#BBBBBB,#EEEEEE); + background-image: -webkit-linear-gradient(bottom,#BBBBBB,#EEEEEE); + filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#BBBBBB, endColorstr=#EEEEEE); /* IE 6-8 */ +} +.tabitem { + display:inline-block; + background:white; + + height:20px; + border-top:2px solid white; + border-right:1px solid lightgrey; + margin-top:2px; + padding:0 8px; +} +.tabitem:hover { + border-top:2px solid #EEE; +} +.tabitem.selected { + border-top:2px solid darkgreen; +} +.tabitem.unread { + background:lightyellow; +} +.tab-label { + cursor:pointer; +} +.tab-closer { + background:#F8F8F8; + color:grey; + font-weight:bold; + cursor:pointer; + + display:inline-block; + *display:block; /* IE7 */ + *zoom:1; /* IE7 */ + + text-align:center; + + /* Cosmetic */ + + padding:0 3px; + border-radius:12px; + border:1px solid white; + line-height:12px; + + position:relative; + top:1px; + + -webkit-transition:0.1s linear all; + -moz-transition:0.1s linear all; + transition:0.1s linear all; +} +.tab-closer:hover { + background:#FFDDDD; + border:1px solid red; + color:black; +} +/* */ +.content { + display:block; +} +.content-inner { + overflow:hidden; + overflow-y:auto; + -webkit-transition: all 0.25s ease-in-out; + + padding:0; /*4px;*/ + margin:0; + box-sizing:border-box; + + position:absolute; + top:4px; + bottom:0; + left:4px; + right:0; +} +/* */ +#basebar { + border-top: 1px solid #999; + + text-align:center; + font-weight:bold; + color:white; + vertical-align:middle; + + background: #ACB9CA; + background-image: linear-gradient(bottom,#6E85A2,#ACB9CA); + background-image: -ms-linear-gradient(bottom,#6E85A2,#ACB9CA); + background-image: -moz-linear-gradient(bottom,#6E85A2,#ACB9CA); + background-image: -webkit-linear-gradient(bottom,#6E85A2,#ACB9CA); + filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#6E85A2, endColorstr=#ACB9CA); /* IE 6-8 */ +} +#form-none { + height:32px; + position:absolute; + right:4px; + left:4px; + top:4px; +} +#chatbox-container { + padding:1px; + + padding-right: 15px; /* (6 + 1)*2 plus 1 expected */ + + position:absolute; + left:0;top:0;bottom:0;right:32px; +} +#chatbox { + font-size:1.2em; + font-family:Geneva,Helvetica,Arial,sans-serif; + + border:1px; + border-radius:3px; + padding:0 6px; + margin:0; + + display:block; + width:100%; + height:100%; +} +#submit-container { + position:absolute; + top:0;bottom:0;right:0;width:32px; +} +#btsubmit { + width:100%; + height:100%; + + font-size:1.2em; + font-family:Geneva,Helvetica,Arial,sans-serif; +} + +/* iOS-style list view */ + +.ul-large { + background-color: #e9e9e9; + border: 1px solid #ccc; + width: 95%; + display: block; + position: relative; + padding: 0; + margin-left: auto; + margin-right: auto; + -webkit-border-radius: 12px; + -moz-border-radius: 12px; + border-radius: 12px; +} +.ul-large li { + color: #333; + font: bold 15px Geneva, Arial, Helvetica, sans-serif; + vertical-align: middle; + border-bottom: 1px solid #ccc; + list-style-type: none; + text-align: left; + white-space: nowrap; + overflow: hidden; + height: 30px; + list-style-type: none; + display: block; + padding: 15px 5px 5px 10px; +} +.ul-large li:first-child { + border-top: 0; + -webkit-border-top-left-radius: 12px; + -webkit-border-top-right-radius: 12px; + -moz-border-top-left-radius: 12px; + -moz-border-top-right-radius: 12px; + border-top-left-radius: 12px; + border-top-right-radius: 12px; +} +.ul-large li:last-child { + border-bottom: 0; + -webkit-border-bottom-left-radius: 12px; + -webkit-border-bottom-right-radius: 12px; + -moz-border-bottom-left-radius: 12px; + -moz-border-bottom-right-radius: 12px; + border-bottom-left-radius: 12px; + border-bottom-right-radius: 12px; +} + +.ul-large li { + cursor:pointer; +} + +/* Text */ + +.tx-time { + color:grey; +} +.tx-sys { + font-weight: bold; + color: darkgreen; +} +.tx-user { + font-weight: bold; + color: black; +} +.tx-chat { + color: black; +} +.tx-private { + color: red; + font-style: italic; +} + +/* webkit scrollbars */ + +::-webkit-scrollbar-track-piece{ + /*background-color:#fff;*/ + -webkit-border-radius:0; +} +::-webkit-scrollbar{ + width:8px; + height:8px; +} +::-webkit-scrollbar-thumb{ + height:50px; + background-color:#CCC; + -webkit-border-radius:4px; + outline:2px solid #fff; + outline-offset:-2px; + border: 2px solid #fff; + + -webkit-transition:background-color 0.1s; + transition:background-color 0.1s; +} +::-webkit-scrollbar-thumb:hover{ + height:50px; + background-color:#999; + -webkit-border-radius:4px; +} + +/* Userlist */ + +#panel-userlist { + border-left:1px solid #EEE; + background:#F8F8F8; + overflow-y: auto; +} + +/* User sprite */ + +.ul-mini { + padding-left: 4px; + margin-top:4px; +} +.ul-mini li { + list-style-type:none; + min-height:13px; + line-height:13px; + padding-left:14px; + margin-bottom:5px; + cursor:pointer; + + background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAANCAYAAACQN/8FAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABh0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzT7MfTgAAAZJJREFUKJFtz71rE2EAgPHnrknbXBKkLWhx0DhoWsEOxk0taMGlQ7cimElK5uroH5DFSUQ3KQpV2qlQEDJ4mKFQ0MVS6kfN1dQP2ss1ydW79y6Xu3udBG36zL/lUaSUVKtVqes6hmGgaRqlUulUoVAw+ScV4P2bFfrEPhnF4+v2NuVy+QFHSjiNXWV2cgzh+dRqX1hsNflcr5/tgcJ14larSdDxCYIOuWEV0c3+7oEAxk4NANu2GUnFFIt3FnvgydxFZb9hSRmFCCGwmjb3i3OVo1AFyF2bZm/PxDRN4rDDo4WHt4+F77RfmKNZ1MQA9ukTPPeXXt14OvXita5f+guVJ+uPZ1brlZUJI08qG/F26AP9chDXO8RpeyzNvMyMnc+7fembmU9WYJFXL7M1YtBNhqRIEYURh1Gb/u+DH69fubqRaLQOcAccHDy++TW0pEaATzcOkFHMxs/NCQD13oV50laaH/EuQdhBhAIRClzF/W9GkVIC8Gx54dZafa3SDmxc1SEdZxgfHmdu+m7y3Jlc+AextbuG6q1RSQAAAABJRU5ErkJggg==') no-repeat 0 0; +} +.ul-mini li:hover { + background-color:#EEE; +} + +/* Display userlist alongside main chat for wide screens */ + +#panel-userlist { + display:none; +} +@media screen and (min-width: 590px) { + + .placement-vary { + right:150px; + } + #panel-userlist { + display:block; + } + #tabitem-tab-users { + display:none; + } + .position-panel { + display:block; + } +} \ No newline at end of file diff --git a/client/dcwebui.js b/client/dcwebui.js new file mode 100644 index 0000000..2aea532 --- /dev/null +++ b/client/dcwebui.js @@ -0,0 +1,747 @@ +/* dcwebui.js */ +//;(function() { + +"use strict"; + +var $ = (document.querySelectorAll ? + function(s) { + var r = document.querySelectorAll(s); + return (s[0] === '#' && r.length === 1) ? r[0] : r; + } : + function(s) { + // i'm not writing a selector engine... + if (! s.length) return []; + if (s[0] === '#') { + return document.getElementById(s.slice(1)); + } else if (s[0] === '.') { + return document.getElementsByClassName(s.slice(1)); + } else { + return document.getElementsByTagName(s); + } + } +); + +var nmdc_escape = function(str) { + return (''+str).length ? (''+str). + replace(/&/g,'&').replace(/\|/g,'|').replace(/\$/g,'$') : + ' '; +}; + +var hesc = function(s) { + var filter = { + '&': '&', '<': '<', '>': '>', '"': '"', '\'': ''' + }; + return s.toString().replace(/[&<>'"]/g, function(s) { return filter[s]; }); +}; + +var nl2br = function(str) { // thanks php.js! + return (str+'').replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1
$2'); +}; + +var linkify = function(str) { + return str.replace( + /(https?:\/\/[^\s<]+)/g, "$1" + ); +}; + +var sanitise = function(s) { + return linkify(nl2br(hesc(s))); +}; + +var toggle = function($el) { + $el.style.display = ($el.style.display === "block") ? "none" : "block"; +}; + +var textContent = function($el) { + if ($el.textContent) return $el.textContent; + if ($el.innerText) return $el.innerText; + return ""; +}; + +// https://gist.github.com/eligrey/1276030 +var appendInnerHTML = function($el, html) { + var child = document.createElement("span"); + child.innerHTML = html; + + var node; + while ((node = child.firstChild)) { + $el.appendChild(node); + } +}; + +// http://stackoverflow.com/a/5598797 +function getOffsetLeft( elem ) { + var offsetLeft = 0; + do { + if (!isNaN(elem.offsetLeft)) { + offsetLeft += elem.offsetLeft; + } + } while (elem = elem.offsetParent); + return offsetLeft; +} + +function getOffsetTop( elem ) { + var offsetTop = 0; + do { + if (!isNaN(elem.offsetTop)) { + offsetTop += elem.offsetTop; + } + } while (elem = elem.offsetParent); + return offsetTop; +} + +/* Tab writers */ + +var write = function(tab) { + var $tab = $('#inner-'+tab); + return { + 'cls': function() { + $tab.innerHTML = ''; + return this; + }, + 'scroll': function() { + $tab.scrollTop = $tab.scrollHeight; + return this; + }, + 'raw': function(s) { + appendInnerHTML($tab, s); + return this.scroll(); + }, + 'c': function(c, s) { + return this.raw(''+s+''); + }, + 'time': function() { + var d = new Date(); + var pad = function(s) { + return (s < 10) ? '0'+s : ''+s ; + }; + return this.raw( + '['+ + pad(d.getHours()) + ":" + pad(d.getMinutes())+ + "] " + ); + }, + 'system': function(s) { + return this.time().c('tx-sys', sanitise(s)).raw('
'); + }, + 'pubnick': function(u) { + return this.raw('<'+hesc(u)+'>'); + }, + 'pub': function(u, s) { + return this.time(). + pubnick(u).raw(' '). + c('tx-chat', sanitise(s)).raw('
'); + }, + 'priv': function(f, t, s) { + return this.time(). + c('tx-user', '<'+hesc(f)+' -> '+hesc(t)+'>'). + raw(' '). + c('tx-private', sanitise(s)). + raw('
'); + } + }; +}; + +/* Userlist */ + +var switchToPM = function(u) { + writerFor(u); // create + tab_set(pm_tabs[u]); // switch + writerFor(u).scroll(); // scroll +}; + +var userMenu = function(u, ev) { + + usermenu.hide(); + usermenu = new MenuList(ev.target); + usermenu.add("Send private message...", function() { + switchToPM(u); + }); + // Usercommands + for (var i = 0; i < user_usercommands.length; i++) (function(i) { + var raw = user_usercommands[i].raw; + usermenu.add(user_usercommands[i].title, function() { + + var message = usercommand_process( raw .replace(/%\[nick\]/g, u) ); + sock.emit('raw', {message: message}); + }); + })(i); + + // Show + usermenu.show(); + + // + ev.preventDefault(); + return false; +}; + +var userlist = { + 'add': function(u) { + if (this.has(u)) return; + + var userlists = $(".userlist"); + for (var l = 0, e = userlists.length; l !== e; ++l) { + var userlist = userlists[l]; + + var to_add = document.createElement('li'); + to_add.innerHTML = hesc(u); + + to_add.onclick = function() { switchToPM(u); }; + to_add.oncontextmenu = function(ev) { return userMenu(u, ev); }; + + var users = userlist.children; + var cmp = hesc(u).toUpperCase(); + + var found = false; + for (var i = 0; i < users.length; i++) { + if ((''+users[i].innerHTML).toUpperCase() > cmp) { + userlist.insertBefore(to_add, users[i]); + found = true; + break; + } + } + if (! found) { + userlist.appendChild(to_add); + } + } + return this; + }, + 'del': function(u) { + var userlists = $(".userlist"); + for (var l = 0, e = userlists.length; l !== e; ++l) { + if (! userlists[l].children) continue; + var userlist = userlists[l]; + + var users = userlist.children; + var cmp = hesc(u).toUpperCase(); + for (var i = 0; i < users.length; i++) { + if ((''+users[i].innerHTML).toUpperCase() === cmp) { + userlist.removeChild(users[i]); + break; + } + } + } + return this; + }, + 'clear': function() { + var userlists = $(".userlist"); + for (var i in userlists) { + if (! userlists[i].children) continue; + var userlist = userlists[i]; + + var users = userlist.children; + for (var j = 0; j < users.length; j++) { + userlist.removeChild(users[j]); + } + } + return this; + }, + 'names': function() { + var userlist = $(".userlist")[0].children; + var ret = []; + for (var i = 0, e = userlist.length; i < e; ++i) { + ret.push( textContent(userlist[i]) ); + } + return ret; + }, + 'has': function(u) { + var userlist = $(".userlist")[0].children; + for (var i = 0, e = userlist.length; i < e; ++i) { + if (textContent(userlist[i]) === u) { + return true; + } + } + return false; + }, + 'count': function() { + return $(".userlist")[0].children.length; + } +}; + +var submit = function() { + var str = $("#chatbox").value; + if (! str.length) return; + + if (hub_state === 0) { + hub_state = 1; + + var name_parts = str.split(":", 2); + hub_last_nick = name_parts[0]; + + sock.emit('hello', {'nick' : hub_last_nick, 'pass' : name_parts.length >= 2 ? name_parts[1] : ''}); + $("#chatbox").disabled = true; + write("tab-main").system("Connecting..."); + + } else if (hub_state === 2) { + if (pm_target !== false) { + sock.emit('priv', {'user': pm_target, 'message': str}); + writerFor(pm_target).pub(hub_last_nick, str ); + } else { + sock.emit('pub', {'message' : str}); + } + + } else { + write("tab-main").system("Invalid internal state."); + } + + $("#chatbox").value = ''; +}; + +/* tabs */ + +/** + * Switch active tab + * + * @param {String} tab Full tab ID (e.g. tab-main, tab-users, tab-ext-???) + */ +var tab_set = function(tab) { + + var tabs = $(".tabpane"); + for (var i in tabs) { + try { + tabs[i].style.display = (tabs[i].id === tab ? 'block' : 'none'); + } catch (e) {}; + } + var tabitems = $(".tabitem"); + for (var i in tabitems) { + try { + tabitems[i].className = "tabitem" + (tabitems[i].getAttribute('data-tab') === tab ? ' selected' : ''); + } catch (e) {}; + } + + pm_target = false; + for (var i in pm_tabs) { + if (pm_tabs[i] === tab) { + pm_target = i; + break; + } + } + + updateTitle(); + + write(tab).scroll(); + $("#chatbox").focus(); + last_tab = tab; +}; + +var tab_new = function(id, name) { + appendInnerHTML($("#bar"), + '
'+ + ''+ + hesc(name)+ + ' '+ + '×'+ + '
' + ); + appendInnerHTML($("#extratabs"), + ' ' + ); + tab_addHandlers(); + return "tab-ext-"+id; +}; + +var tab_free = function(id) { + if (id === "tab-main") return; + + // remove tab item and body + var el = $("#tabitem-"+id); + el.parentNode.removeChild(el); + + var el = $("#"+id); + el.parentNode.removeChild(el); + + // clear from PM tabs + for (var i in pm_tabs) { + if (pm_tabs[i] === id) { + pm_tabs[i] = false; + } + } + + // maybe clear the 'new pm' warning + updateTitle(); + + // don't leave us with no tab displayed + if (last_tab === id) { + tab_set("tab-main"); + } +}; + +var tab_addHandlers = function() { + var tabitems = $(".tabitem"); + for (var i = 0; i < tabitems.length; i++) { + if (! tabitems[i]) continue; + + tabitems[i].onclick = function(ev) { + tab_set( this.getAttribute('data-tab') ); + + // 360nobubble + if (ev.stopPropagation) { + ev.stopPropagation(); + } else { + ev.cancelBubble = true; // oldIE + } + return false; + }; + } + + var tabclosers = $(".tab-closer"); + for (var i = 0; i < tabclosers.length; i++) { + if (! tabclosers[i]) continue; + + tabclosers[i].onclick = function(ev) { + tab_free( this.getAttribute('data-tab') ); + + // 360nobubble + if (ev.stopPropagation) { + ev.stopPropagation(); + } else { + ev.cancelBubble = true; // oldIE + } + return false; + }; + } +}; + +/* */ + +var writerFor = function(username) { + var tabid = ""; + if (! username in pm_tabs || ! pm_tabs[username]) { + tabid = tab_new(next_tabid++, username); + pm_tabs[username] = tabid; + } else { + tabid = pm_tabs[username]; + } + + return write(tabid); +}; + +/* */ + +var tabcomplete_state = ''; + +var tabcompletion_start = function() { + + var cursor = $("#chatbox").value.replace(/^.*\s([^\s]+)$/, '$1'); + + if (tabcomplete_state === '') { + // new tab completion + tabcomplete_state = cursor; + } + + // Find all users who start with tabcomplete_state and retrieve the first + // one after cursor + + var users = userlist.names(); + var match = []; + + for (var i = 0, e = users.length; i !== e; ++i) { + if (users[i].toUpperCase().indexOf(tabcomplete_state.toUpperCase()) === 0) { + match.push(users[i]); + } + } + + if (match.length === 0) { + // no matches + return; + } + + // Is cursor in the list? + var cpos = -1; + for (var i = 0, e = match.length; i !== e; ++i) { + if (match[i] === cursor) { + cpos = i; break; + } + } + + var targetName = match[(i + 1) % match.length]; + + // Replace in textbox + + var chatprefix = $("#chatbox").value.substr(0, $("#chatbox").value.length - cursor.length); + + $("#chatbox").value = chatprefix + targetName; + $("#chatbox").focus(); +}; + +var tabcompletion_inactive = function() { + tabcomplete_state = ''; +}; + +/* */ + +var MenuList = function(el) { + this.el = el; + + this.div = document.createElement("div"); + this.div.classList.add("menu"); + this.div.style.position = "absolute"; + + this.ul = document.createElement("ul"); + this.div.appendChild(this.ul); + + document.body.appendChild(this.div); + +}; +MenuList.prototype.clear = function() { + while (this.ul.children.length) { + this.ul.children[0].parentNode.removeChild( this.ul.children[0] ); + } +}; +MenuList.prototype.add = function(txt, cb) { + var li = document.createElement("li"); + li.innerHTML = txt; + li.onclick = cb; + this.ul.appendChild(li); +}; +MenuList.prototype.show = function() { + this.div.style.display = "block"; + + this.div.style.top = (getOffsetTop(this.el) + this.el.clientHeight)+"px"; + this.div.style.left = (getOffsetLeft(this.el) - 200 + this.el.clientWidth)+"px"; +}; +MenuList.prototype.hide = function() { + this.div.style.display = "none"; +}; +MenuList.prototype.toggle = function() { + // ES5 strict mode sets `this` to undefined + (this.div.style.display === "block" ? this.hide : this.show).call(this); +}; + +/* */ + +var menu = new MenuList($("#menubutton")); + +menu.reset = function() { + this.clear(); + this.add(joinparts_getstr(), toggle_joinparts); +}; + +var usermenu = new MenuList(document.body); + +/** + * Process all substitutions in a usercommand. May prompt the user for input. + * + * @param {String} str + * @returns {String} + */ +var usercommand_process = function(str) { + var message = str + .replace(/%\[mynick\]/g, hub_last_nick) + ; + var match = null; + for(;;) { + match = message.match(/%\[line:([^\]]*)\]/); + if (match === null) break; + + var res = prompt(match[1]); + if (res === null) return; //cancelled + + message = + message.substr(0, match.index) + + nmdc_escape(res) + + message.substr(match.index + match[0].length) + ; + } + + return message; +}; + +var user_usercommands = []; + +/** + * Called when a $UserCommand is recieved from the server. Register it in all + * relevant context menus. + * + * @param {Object} data Has properties type, context, title, raw + * @returns {undefined} + */ +var process_usercommand = function(data) { + switch(data.type) { + case 0: { /* USERCOMMAND_TYPE_SEPARATOR */ + // ignore + } break; + + case 1: /* USERCOMMAND_TYPE_RAW */ + case 2:/* USERCOMMAND_TYPE_NICKLIMITED */ + { + if (data.context & 1) { /* USERCOMMAND_CONTEXT_HUB */ + menu.add(data.title, function() { + sock.emit('raw', {message: usercommand_process(data.raw)}); + }); + } + + if (data.context & 2) { /* USERCOMMAND_CONTEXT_USER */ + user_usercommands.push(data); + } + } break; + + case 255: { /* USERCOMMAND_TYPE_CLEARALL */ + if (data.context & 1) { + menu.reset(); + } + + if (data.context & 2) { + user_usercommands = []; // clear + } + } break; + } +}; + +var joinparts_getstr = function() { + return (show_joins ? "☑" : "☐") + " Show Joins/Parts"; +}; + +var toggle_joinparts = function(ev) { + var $el = ev.target || ev.srcElement; + show_joins = ! show_joins; + $el.innerHTML = joinparts_getstr(); +}; + +/* */ + +var updateTitle = function() { + document.title = + ($(".unread").length ? "[NEW PM] " : "") + + "("+userlist.count()+") "+ + hub_hubname; +}; + +var sock = {}; +var hub_state = 0; // [disconnected, sent-nick, connected] +var hub_last_nick = ''; +var hub_hubname = 'DCWebUI'; + +var pm_tabs = {}; // nick => tabid +var next_tabid = 1; +var pm_target = false; + +var last_tab = "tab-main"; + +var show_joins = false; + +window.onload = function() { + write("tab-main").system("Communicating with server..."); + + document.title = DCWEBUI_CONF.title; + + // HTML event handlers + + $("#form-none").onsubmit = function(ev) { + submit(); + + // don't submit form + ev.preventDefault(); + return false; + }; + + $("#chatbox").onkeydown = function(ev) { + if (ev.keyCode === 9) { + // tab + tabcompletion_start(); + ev.preventDefault(); + return false; + + } else { + tabcompletion_inactive(); + } + }; + + window.onresize = function() { + if (last_tab === "tab-users" && document.documentElement.clientWidth >= 590) { + tab_set("tab-main"); + } + + menu.hide(); + usermenu.hide(); + }; + + $("#menubutton").onclick = function(ev) { + menu.toggle(); + ev.preventDefault(); + ev.stopPropagation(); + return false; + }; + + window.onclick = function() { + menu.hide(); + usermenu.hide(); + }; + + menu.reset(); + + tab_addHandlers(); + + // Hacks for WiiU user-agent + + if (navigator && navigator.userAgent && !!navigator.userAgent.match("Nintendo WiiU")) { + document.body.className += " navigator-wiiu"; + } + + // Socket event handlers + + sock = io.connect(DCWEBUI_CONF.extern); + sock.on('cls', function() { + write("tab-main").cls(); + userlist.clear(); + hub_state = 0; + }); + sock.on('hubname', function(s) { + write("tab-main").system("Now talking on "+s); + hub_hubname = s; + updateTitle(); + }); + sock.on('sys', function(data) { + write("tab-main").system(data); + }); + sock.on('raw', function(data) { + write("tab-main").raw(data); + }); + sock.on('pub', function(data) { + write("tab-main").pub( data.user, data.message); + }); + sock.on('priv', function(data) { + writerFor(data.user).pub( data.user, data.message); + if (last_tab !== pm_tabs[data.user] && + ($("#tabitem-"+pm_tabs[data.user]).className.indexOf('unread') === -1) + ) { + $("#tabitem-"+pm_tabs[data.user]).className += " unread"; + updateTitle(); + } + }); + sock.on('hello', function() { + hub_state = 2; + $("#chatbox").disabled = false; + }); + sock.on('part', function(u) { + userlist.del(u.user); + if (show_joins) { + write("tab-main").system("*** Parts: "+u.user); // sanitised + } + updateTitle(); + }); + sock.on('join', function(u) { + userlist.add(u.user); + if (show_joins) { + write("tab-main").system("*** Joins: "+u.user); + } + updateTitle(); + }); + sock.on('close', function() { + hub_state = 0; + userlist.clear(); + write("tab-main").system("Connection closed by remote host."); + }); + sock.on('disconnect', function() { + hub_state = 0; + userlist.clear(); + write("tab-main").system("Lost connection to the server."); + }); + sock.on('usercommand', function(data) { + process_usercommand(data); + }); +}; + +//})(); diff --git a/client/favicon.ico b/client/favicon.ico new file mode 100644 index 0000000..0290130 Binary files /dev/null and b/client/favicon.ico differ diff --git a/client/index.htm b/client/index.htm new file mode 100644 index 0000000..49699d2 --- /dev/null +++ b/client/index.htm @@ -0,0 +1,59 @@ + + + + + + + + + + Loading... + + +
+ +
+ + Main + +
+
+ + Users + +
+
+ +
+
+
+
+
+
    +
    +
    + + + +
    + +
    +
    +
    + +
    +
    + +
    +
    +
    + + + + + + \ No newline at end of file diff --git a/client/socket.io-1.4.5.js b/client/socket.io-1.4.5.js new file mode 100644 index 0000000..6f478d8 --- /dev/null +++ b/client/socket.io-1.4.5.js @@ -0,0 +1,4 @@ +(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.io=f()}})(function(){var define,module,exports;return function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o0){this.extraHeaders=opts.extraHeaders}}this.open()}Socket.priorWebsocketSuccess=false;Emitter(Socket.prototype);Socket.protocol=parser.protocol;Socket.Socket=Socket;Socket.Transport=_dereq_("./transport");Socket.transports=_dereq_("./transports");Socket.parser=_dereq_("engine.io-parser");Socket.prototype.createTransport=function(name){debug('creating transport "%s"',name);var query=clone(this.query);query.EIO=parser.protocol;query.transport=name;if(this.id)query.sid=this.id;var transport=new transports[name]({agent:this.agent,hostname:this.hostname,port:this.port,secure:this.secure,path:this.path,query:query,forceJSONP:this.forceJSONP,jsonp:this.jsonp,forceBase64:this.forceBase64,enablesXDR:this.enablesXDR,timestampRequests:this.timestampRequests,timestampParam:this.timestampParam,policyPort:this.policyPort,socket:this,pfx:this.pfx,key:this.key,passphrase:this.passphrase,cert:this.cert,ca:this.ca,ciphers:this.ciphers,rejectUnauthorized:this.rejectUnauthorized,perMessageDeflate:this.perMessageDeflate,extraHeaders:this.extraHeaders});return transport};function clone(obj){var o={};for(var i in obj){if(obj.hasOwnProperty(i)){o[i]=obj[i]}}return o}Socket.prototype.open=function(){var transport;if(this.rememberUpgrade&&Socket.priorWebsocketSuccess&&this.transports.indexOf("websocket")!=-1){transport="websocket"}else if(0===this.transports.length){var self=this;setTimeout(function(){self.emit("error","No transports available")},0);return}else{transport=this.transports[0]}this.readyState="opening";try{transport=this.createTransport(transport)}catch(e){this.transports.shift();this.open();return}transport.open();this.setTransport(transport)};Socket.prototype.setTransport=function(transport){debug("setting transport %s",transport.name);var self=this;if(this.transport){debug("clearing existing transport %s",this.transport.name);this.transport.removeAllListeners()}this.transport=transport;transport.on("drain",function(){self.onDrain()}).on("packet",function(packet){self.onPacket(packet)}).on("error",function(e){self.onError(e)}).on("close",function(){self.onClose("transport close")})};Socket.prototype.probe=function(name){debug('probing transport "%s"',name);var transport=this.createTransport(name,{probe:1}),failed=false,self=this;Socket.priorWebsocketSuccess=false;function onTransportOpen(){if(self.onlyBinaryUpgrades){var upgradeLosesBinary=!this.supportsBinary&&self.transport.supportsBinary;failed=failed||upgradeLosesBinary}if(failed)return;debug('probe transport "%s" opened',name);transport.send([{type:"ping",data:"probe"}]);transport.once("packet",function(msg){if(failed)return;if("pong"==msg.type&&"probe"==msg.data){debug('probe transport "%s" pong',name);self.upgrading=true;self.emit("upgrading",transport);if(!transport)return;Socket.priorWebsocketSuccess="websocket"==transport.name;debug('pausing current transport "%s"',self.transport.name);self.transport.pause(function(){if(failed)return;if("closed"==self.readyState)return;debug("changing transport and sending upgrade packet");cleanup();self.setTransport(transport);transport.send([{type:"upgrade"}]);self.emit("upgrade",transport);transport=null;self.upgrading=false;self.flush()})}else{debug('probe transport "%s" failed',name);var err=new Error("probe error");err.transport=transport.name;self.emit("upgradeError",err)}})}function freezeTransport(){if(failed)return;failed=true;cleanup();transport.close();transport=null}function onerror(err){var error=new Error("probe error: "+err);error.transport=transport.name;freezeTransport();debug('probe transport "%s" failed because of error: %s',name,err);self.emit("upgradeError",error)}function onTransportClose(){onerror("transport closed")}function onclose(){onerror("socket closed")}function onupgrade(to){if(transport&&to.name!=transport.name){debug('"%s" works - aborting "%s"',to.name,transport.name);freezeTransport()}}function cleanup(){transport.removeListener("open",onTransportOpen);transport.removeListener("error",onerror);transport.removeListener("close",onTransportClose);self.removeListener("close",onclose);self.removeListener("upgrading",onupgrade)}transport.once("open",onTransportOpen);transport.once("error",onerror);transport.once("close",onTransportClose);this.once("close",onclose);this.once("upgrading",onupgrade);transport.open()};Socket.prototype.onOpen=function(){debug("socket open");this.readyState="open";Socket.priorWebsocketSuccess="websocket"==this.transport.name;this.emit("open");this.flush();if("open"==this.readyState&&this.upgrade&&this.transport.pause){debug("starting upgrade probes");for(var i=0,l=this.upgrades.length;i';iframe=document.createElement(html)}catch(e){iframe=document.createElement("iframe");iframe.name=self.iframeId;iframe.src="javascript:0"}iframe.id=self.iframeId;self.form.appendChild(iframe);self.iframe=iframe}initIframe();data=data.replace(rEscapedNewline,"\\\n");this.area.value=data.replace(rNewline,"\\n");try{this.form.submit()}catch(e){}if(this.iframe.attachEvent){this.iframe.onreadystatechange=function(){if(self.iframe.readyState=="complete"){complete()}}}else{this.iframe.onload=complete}}}).call(this,typeof self!=="undefined"?self:typeof window!=="undefined"?window:typeof global!=="undefined"?global:{})},{"./polling":8,"component-inherit":16}],7:[function(_dereq_,module,exports){(function(global){var XMLHttpRequest=_dereq_("xmlhttprequest-ssl");var Polling=_dereq_("./polling");var Emitter=_dereq_("component-emitter");var inherit=_dereq_("component-inherit");var debug=_dereq_("debug")("engine.io-client:polling-xhr");module.exports=XHR;module.exports.Request=Request;function empty(){}function XHR(opts){Polling.call(this,opts);if(global.location){var isSSL="https:"==location.protocol;var port=location.port;if(!port){port=isSSL?443:80}this.xd=opts.hostname!=global.location.hostname||port!=opts.port;this.xs=opts.secure!=isSSL}else{this.extraHeaders=opts.extraHeaders}}inherit(XHR,Polling);XHR.prototype.supportsBinary=true;XHR.prototype.request=function(opts){opts=opts||{};opts.uri=this.uri();opts.xd=this.xd;opts.xs=this.xs;opts.agent=this.agent||false;opts.supportsBinary=this.supportsBinary;opts.enablesXDR=this.enablesXDR;opts.pfx=this.pfx;opts.key=this.key;opts.passphrase=this.passphrase;opts.cert=this.cert;opts.ca=this.ca;opts.ciphers=this.ciphers;opts.rejectUnauthorized=this.rejectUnauthorized;opts.extraHeaders=this.extraHeaders;return new Request(opts)};XHR.prototype.doWrite=function(data,fn){var isBinary=typeof data!=="string"&&data!==undefined;var req=this.request({method:"POST",data:data,isBinary:isBinary});var self=this;req.on("success",fn);req.on("error",function(err){self.onError("xhr post error",err)});this.sendXhr=req};XHR.prototype.doPoll=function(){debug("xhr poll");var req=this.request();var self=this;req.on("data",function(data){self.onData(data)});req.on("error",function(err){self.onError("xhr poll error",err)});this.pollXhr=req};function Request(opts){this.method=opts.method||"GET";this.uri=opts.uri;this.xd=!!opts.xd;this.xs=!!opts.xs;this.async=false!==opts.async;this.data=undefined!=opts.data?opts.data:null;this.agent=opts.agent;this.isBinary=opts.isBinary;this.supportsBinary=opts.supportsBinary;this.enablesXDR=opts.enablesXDR;this.pfx=opts.pfx;this.key=opts.key;this.passphrase=opts.passphrase;this.cert=opts.cert;this.ca=opts.ca;this.ciphers=opts.ciphers;this.rejectUnauthorized=opts.rejectUnauthorized;this.extraHeaders=opts.extraHeaders;this.create()}Emitter(Request.prototype);Request.prototype.create=function(){var opts={agent:this.agent,xdomain:this.xd,xscheme:this.xs,enablesXDR:this.enablesXDR};opts.pfx=this.pfx;opts.key=this.key;opts.passphrase=this.passphrase;opts.cert=this.cert;opts.ca=this.ca;opts.ciphers=this.ciphers;opts.rejectUnauthorized=this.rejectUnauthorized;var xhr=this.xhr=new XMLHttpRequest(opts);var self=this;try{debug("xhr open %s: %s",this.method,this.uri);xhr.open(this.method,this.uri,this.async);try{if(this.extraHeaders){xhr.setDisableHeaderCheck(true);for(var i in this.extraHeaders){if(this.extraHeaders.hasOwnProperty(i)){xhr.setRequestHeader(i,this.extraHeaders[i])}}}}catch(e){}if(this.supportsBinary){xhr.responseType="arraybuffer"}if("POST"==this.method){try{if(this.isBinary){xhr.setRequestHeader("Content-type","application/octet-stream")}else{xhr.setRequestHeader("Content-type","text/plain;charset=UTF-8")}}catch(e){}}if("withCredentials"in xhr){xhr.withCredentials=true}if(this.hasXDR()){xhr.onload=function(){self.onLoad()};xhr.onerror=function(){self.onError(xhr.responseText)}}else{xhr.onreadystatechange=function(){if(4!=xhr.readyState)return;if(200==xhr.status||1223==xhr.status){self.onLoad()}else{setTimeout(function(){self.onError(xhr.status)},0)}}}debug("xhr data %s",this.data);xhr.send(this.data)}catch(e){setTimeout(function(){self.onError(e)},0);return}if(global.document){this.index=Request.requestsCount++;Request.requests[this.index]=this}};Request.prototype.onSuccess=function(){this.emit("success");this.cleanup()};Request.prototype.onData=function(data){this.emit("data",data);this.onSuccess()};Request.prototype.onError=function(err){this.emit("error",err);this.cleanup(true)};Request.prototype.cleanup=function(fromError){if("undefined"==typeof this.xhr||null===this.xhr){return}if(this.hasXDR()){this.xhr.onload=this.xhr.onerror=empty}else{this.xhr.onreadystatechange=empty}if(fromError){try{this.xhr.abort()}catch(e){}}if(global.document){delete Request.requests[this.index]}this.xhr=null};Request.prototype.onLoad=function(){var data;try{var contentType;try{contentType=this.xhr.getResponseHeader("Content-Type").split(";")[0]}catch(e){}if(contentType==="application/octet-stream"){data=this.xhr.response}else{if(!this.supportsBinary){data=this.xhr.responseText}else{try{data=String.fromCharCode.apply(null,new Uint8Array(this.xhr.response))}catch(e){var ui8Arr=new Uint8Array(this.xhr.response);var dataArray=[];for(var idx=0,length=ui8Arr.length;idxbytes){end=bytes}if(start>=bytes||start>=end||bytes===0){return new ArrayBuffer(0)}var abv=new Uint8Array(arraybuffer);var result=new Uint8Array(end-start);for(var i=start,ii=0;i>2]; +base64+=chars[(bytes[i]&3)<<4|bytes[i+1]>>4];base64+=chars[(bytes[i+1]&15)<<2|bytes[i+2]>>6];base64+=chars[bytes[i+2]&63]}if(len%3===2){base64=base64.substring(0,base64.length-1)+"="}else if(len%3===1){base64=base64.substring(0,base64.length-2)+"=="}return base64};exports.decode=function(base64){var bufferLength=base64.length*.75,len=base64.length,i,p=0,encoded1,encoded2,encoded3,encoded4;if(base64[base64.length-1]==="="){bufferLength--;if(base64[base64.length-2]==="="){bufferLength--}}var arraybuffer=new ArrayBuffer(bufferLength),bytes=new Uint8Array(arraybuffer);for(i=0;i>4;bytes[p++]=(encoded2&15)<<4|encoded3>>2;bytes[p++]=(encoded3&3)<<6|encoded4&63}return arraybuffer}})("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/")},{}],14:[function(_dereq_,module,exports){(function(global){var BlobBuilder=global.BlobBuilder||global.WebKitBlobBuilder||global.MSBlobBuilder||global.MozBlobBuilder;var blobSupported=function(){try{var a=new Blob(["hi"]);return a.size===2}catch(e){return false}}();var blobSupportsArrayBufferView=blobSupported&&function(){try{var b=new Blob([new Uint8Array([1,2])]);return b.size===2}catch(e){return false}}();var blobBuilderSupported=BlobBuilder&&BlobBuilder.prototype.append&&BlobBuilder.prototype.getBlob;function mapArrayBufferViews(ary){for(var i=0;i=31}exports.formatters.j=function(v){return JSON.stringify(v)};function formatArgs(){var args=arguments;var useColors=this.useColors;args[0]=(useColors?"%c":"")+this.namespace+(useColors?" %c":" ")+args[0]+(useColors?"%c ":" ")+"+"+exports.humanize(this.diff);if(!useColors)return args;var c="color: "+this.color;args=[args[0],c,"color: inherit"].concat(Array.prototype.slice.call(args,1));var index=0;var lastC=0;args[0].replace(/%[a-z%]/g,function(match){if("%%"===match)return;index++;if("%c"===match){lastC=index}});args.splice(lastC,0,c);return args}function log(){return"object"===typeof console&&console.log&&Function.prototype.apply.call(console.log,console,arguments)}function save(namespaces){try{if(null==namespaces){exports.storage.removeItem("debug")}else{exports.storage.debug=namespaces}}catch(e){}}function load(){var r;try{r=exports.storage.debug}catch(e){}return r}exports.enable(load());function localstorage(){try{return window.localStorage}catch(e){}}},{"./debug":18}],18:[function(_dereq_,module,exports){exports=module.exports=debug;exports.coerce=coerce;exports.disable=disable;exports.enable=enable;exports.enabled=enabled;exports.humanize=_dereq_("ms");exports.names=[];exports.skips=[];exports.formatters={};var prevColor=0;var prevTime;function selectColor(){return exports.colors[prevColor++%exports.colors.length]}function debug(namespace){function disabled(){}disabled.enabled=false;function enabled(){var self=enabled;var curr=+new Date;var ms=curr-(prevTime||curr);self.diff=ms;self.prev=prevTime;self.curr=curr;prevTime=curr;if(null==self.useColors)self.useColors=exports.useColors();if(null==self.color&&self.useColors)self.color=selectColor();var args=Array.prototype.slice.call(arguments);args[0]=exports.coerce(args[0]);if("string"!==typeof args[0]){args=["%o"].concat(args)}var index=0;args[0]=args[0].replace(/%([a-z%])/g,function(match,format){if(match==="%%")return match;index++;var formatter=exports.formatters[format];if("function"===typeof formatter){var val=args[index];match=formatter.call(self,val);args.splice(index,1);index--}return match});if("function"===typeof exports.formatArgs){args=exports.formatArgs.apply(self,args)}var logFn=enabled.log||exports.log||console.log.bind(console);logFn.apply(self,args)}enabled.enabled=true;var fn=exports.enabled(namespace)?enabled:disabled;fn.namespace=namespace;return fn}function enable(namespaces){exports.save(namespaces);var split=(namespaces||"").split(/[\s,]+/);var len=split.length;for(var i=0;i1){return{type:packetslist[type],data:data.substring(1)}}else{return{type:packetslist[type]}}}var asArray=new Uint8Array(data);var type=asArray[0];var rest=sliceBuffer(data,1);if(Blob&&binaryType==="blob"){rest=new Blob([rest])}return{type:packetslist[type],data:rest}};exports.decodeBase64Packet=function(msg,binaryType){var type=packetslist[msg.charAt(0)];if(!global.ArrayBuffer){return{type:type,data:{base64:true,data:msg.substr(1)}}}var data=base64encoder.decode(msg.substr(1));if(binaryType==="blob"&&Blob){data=new Blob([data])}return{type:type,data:data}};exports.encodePayload=function(packets,supportsBinary,callback){if(typeof supportsBinary=="function"){callback=supportsBinary;supportsBinary=null}var isBinary=hasBinary(packets);if(supportsBinary&&isBinary){if(Blob&&!dontSendBlobs){return exports.encodePayloadAsBlob(packets,callback)}return exports.encodePayloadAsArrayBuffer(packets,callback)}if(!packets.length){return callback("0:")}function setLengthHeader(message){return message.length+":"+message}function encodeOne(packet,doneCallback){exports.encodePacket(packet,!isBinary?false:supportsBinary,true,function(message){doneCallback(null,setLengthHeader(message))})}map(packets,encodeOne,function(err,results){return callback(results.join(""))})};function map(ary,each,done){var result=new Array(ary.length);var next=after(ary.length,done);var eachWithIndex=function(i,el,cb){each(el,function(error,msg){result[i]=msg;cb(error,result)})};for(var i=0;i0){var tailArray=new Uint8Array(bufferTail);var isString=tailArray[0]===0;var msgLength="";for(var i=1;;i++){if(tailArray[i]==255)break;if(msgLength.length>310){numberTooLong=true;break}msgLength+=tailArray[i]}if(numberTooLong)return callback(err,0,1);bufferTail=sliceBuffer(bufferTail,2+msgLength.length);msgLength=parseInt(msgLength);var msg=sliceBuffer(bufferTail,0,msgLength);if(isString){try{msg=String.fromCharCode.apply(null,new Uint8Array(msg))}catch(e){var typed=new Uint8Array(msg);msg="";for(var i=0;i1e4)return;var match=/^((?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|years?|yrs?|y)?$/i.exec(str);if(!match)return;var n=parseFloat(match[1]);var type=(match[2]||"ms").toLowerCase();switch(type){case"years":case"year":case"yrs":case"yr":case"y":return n*y;case"days":case"day":case"d":return n*d;case"hours":case"hour":case"hrs":case"hr":case"h":return n*h;case"minutes":case"minute":case"mins":case"min":case"m":return n*m;case"seconds":case"second":case"secs":case"sec":case"s":return n*s;case"milliseconds":case"millisecond":case"msecs":case"msec":case"ms":return n}}function short(ms){if(ms>=d)return Math.round(ms/d)+"d";if(ms>=h)return Math.round(ms/h)+"h";if(ms>=m)return Math.round(ms/m)+"m";if(ms>=s)return Math.round(ms/s)+"s";return ms+"ms"}function long(ms){return plural(ms,d,"day")||plural(ms,h,"hour")||plural(ms,m,"minute")||plural(ms,s,"second")||ms+" ms"}function plural(ms,n,name){if(ms=55296&&value<=56319&&counter65535){value-=65536;output+=stringFromCharCode(value>>>10&1023|55296);value=56320|value&1023}output+=stringFromCharCode(value)}return output}function checkScalarValue(codePoint){if(codePoint>=55296&&codePoint<=57343){throw Error("Lone surrogate U+"+codePoint.toString(16).toUpperCase()+" is not a scalar value")}}function createByte(codePoint,shift){return stringFromCharCode(codePoint>>shift&63|128)}function encodeCodePoint(codePoint){if((codePoint&4294967168)==0){return stringFromCharCode(codePoint)}var symbol="";if((codePoint&4294965248)==0){symbol=stringFromCharCode(codePoint>>6&31|192)}else if((codePoint&4294901760)==0){checkScalarValue(codePoint);symbol=stringFromCharCode(codePoint>>12&15|224);symbol+=createByte(codePoint,6)}else if((codePoint&4292870144)==0){symbol=stringFromCharCode(codePoint>>18&7|240);symbol+=createByte(codePoint,12);symbol+=createByte(codePoint,6)}symbol+=stringFromCharCode(codePoint&63|128);return symbol}function utf8encode(string){var codePoints=ucs2decode(string);var length=codePoints.length;var index=-1;var codePoint;var byteString="";while(++index=byteCount){throw Error("Invalid byte index")}var continuationByte=byteArray[byteIndex]&255;byteIndex++;if((continuationByte&192)==128){return continuationByte&63}throw Error("Invalid continuation byte")}function decodeSymbol(){var byte1;var byte2;var byte3;var byte4;var codePoint;if(byteIndex>byteCount){throw Error("Invalid byte index")}if(byteIndex==byteCount){return false}byte1=byteArray[byteIndex]&255;byteIndex++;if((byte1&128)==0){return byte1}if((byte1&224)==192){var byte2=readContinuationByte();codePoint=(byte1&31)<<6|byte2;if(codePoint>=128){return codePoint}else{throw Error("Invalid continuation byte")}}if((byte1&240)==224){byte2=readContinuationByte();byte3=readContinuationByte();codePoint=(byte1&15)<<12|byte2<<6|byte3;if(codePoint>=2048){checkScalarValue(codePoint);return codePoint}else{throw Error("Invalid continuation byte")}}if((byte1&248)==240){byte2=readContinuationByte();byte3=readContinuationByte();byte4=readContinuationByte();codePoint=(byte1&15)<<18|byte2<<12|byte3<<6|byte4;if(codePoint>=65536&&codePoint<=1114111){return codePoint}}throw Error("Invalid UTF-8 detected")}var byteArray;var byteCount;var byteIndex;function utf8decode(byteString){byteArray=ucs2decode(byteString);byteCount=byteArray.length;byteIndex=0;var codePoints=[];var tmp;while((tmp=decodeSymbol())!==false){codePoints.push(tmp)}return ucs2encode(codePoints)}var utf8={version:"2.0.0",encode:utf8encode,decode:utf8decode};if(typeof define=="function"&&typeof define.amd=="object"&&define.amd){define(function(){return utf8})}else if(freeExports&&!freeExports.nodeType){if(freeModule){freeModule.exports=utf8}else{var object={};var hasOwnProperty=object.hasOwnProperty;for(var key in utf8){hasOwnProperty.call(utf8,key)&&(freeExports[key]=utf8[key])}}}else{root.utf8=utf8}})(this)}).call(this,typeof self!=="undefined"?self:typeof window!=="undefined"?window:typeof global!=="undefined"?global:{})},{}],30:[function(_dereq_,module,exports){"use strict";var alphabet="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_".split(""),length=64,map={},seed=0,i=0,prev;function encode(num){var encoded="";do{encoded=alphabet[num%length]+encoded;num=Math.floor(num/length)}while(num>0);return encoded}function decode(str){var decoded=0;for(i=0;i0&&!this.encoding){var pack=this.packetBuffer.shift();this.packet(pack)}};Manager.prototype.cleanup=function(){debug("cleanup");var sub;while(sub=this.subs.shift())sub.destroy();this.packetBuffer=[];this.encoding=false;this.lastPing=null;this.decoder.destroy()};Manager.prototype.close=Manager.prototype.disconnect=function(){debug("disconnect");this.skipReconnect=true;this.reconnecting=false;if("opening"==this.readyState){this.cleanup()}this.backoff.reset();this.readyState="closed";if(this.engine)this.engine.close()};Manager.prototype.onclose=function(reason){debug("onclose");this.cleanup();this.backoff.reset();this.readyState="closed";this.emit("close",reason);if(this._reconnection&&!this.skipReconnect){this.reconnect()}};Manager.prototype.reconnect=function(){if(this.reconnecting||this.skipReconnect)return this;var self=this;if(this.backoff.attempts>=this._reconnectionAttempts){debug("reconnect failed");this.backoff.reset();this.emitAll("reconnect_failed");this.reconnecting=false}else{var delay=this.backoff.duration();debug("will wait %dms before reconnect attempt",delay);this.reconnecting=true;var timer=setTimeout(function(){if(self.skipReconnect)return;debug("attempting reconnect");self.emitAll("reconnect_attempt",self.backoff.attempts);self.emitAll("reconnecting",self.backoff.attempts);if(self.skipReconnect)return;self.open(function(err){if(err){debug("reconnect attempt error");self.reconnecting=false;self.reconnect();self.emitAll("reconnect_error",err.data)}else{debug("reconnect success");self.onreconnect()}})},delay);this.subs.push({destroy:function(){clearTimeout(timer)}})}};Manager.prototype.onreconnect=function(){var attempt=this.backoff.attempts;this.reconnecting=false;this.backoff.reset();this.updateSocketIds();this.emitAll("reconnect",attempt)}},{"./on":33,"./socket":34,backo2:36,"component-bind":37,"component-emitter":38,debug:39,"engine.io-client":1,indexof:42,"socket.io-parser":47}],33:[function(_dereq_,module,exports){module.exports=on;function on(obj,ev,fn){obj.on(ev,fn);return{destroy:function(){obj.removeListener(ev,fn)}}}},{}],34:[function(_dereq_,module,exports){var parser=_dereq_("socket.io-parser");var Emitter=_dereq_("component-emitter");var toArray=_dereq_("to-array");var on=_dereq_("./on");var bind=_dereq_("component-bind");var debug=_dereq_("debug")("socket.io-client:socket");var hasBin=_dereq_("has-binary");module.exports=exports=Socket;var events={connect:1,connect_error:1,connect_timeout:1,connecting:1,disconnect:1,error:1,reconnect:1,reconnect_attempt:1,reconnect_failed:1,reconnect_error:1,reconnecting:1,ping:1,pong:1};var emit=Emitter.prototype.emit;function Socket(io,nsp){this.io=io;this.nsp=nsp;this.json=this;this.ids=0;this.acks={};this.receiveBuffer=[];this.sendBuffer=[];this.connected=false;this.disconnected=true;if(this.io.autoConnect)this.open()}Emitter(Socket.prototype);Socket.prototype.subEvents=function(){if(this.subs)return;var io=this.io;this.subs=[on(io,"open",bind(this,"onopen")),on(io,"packet",bind(this,"onpacket")),on(io,"close",bind(this,"onclose"))]};Socket.prototype.open=Socket.prototype.connect=function(){if(this.connected)return this;this.subEvents();this.io.open();if("open"==this.io.readyState)this.onopen();this.emit("connecting");return this};Socket.prototype.send=function(){var args=toArray(arguments);args.unshift("message");this.emit.apply(this,args);return this};Socket.prototype.emit=function(ev){if(events.hasOwnProperty(ev)){emit.apply(this,arguments);return this}var args=toArray(arguments);var parserType=parser.EVENT;if(hasBin(args)){parserType=parser.BINARY_EVENT}var packet={type:parserType,data:args};packet.options={};packet.options.compress=!this.flags||false!==this.flags.compress;if("function"==typeof args[args.length-1]){debug("emitting packet with ack id %d",this.ids);this.acks[this.ids]=args.pop();packet.id=this.ids++}if(this.connected){this.packet(packet)}else{this.sendBuffer.push(packet)}delete this.flags;return this};Socket.prototype.packet=function(packet){packet.nsp=this.nsp;this.io.packet(packet)};Socket.prototype.onopen=function(){debug("transport is open - connecting");if("/"!=this.nsp){this.packet({type:parser.CONNECT})}};Socket.prototype.onclose=function(reason){debug("close (%s)",reason);this.connected=false;this.disconnected=true;delete this.id;this.emit("disconnect",reason)};Socket.prototype.onpacket=function(packet){if(packet.nsp!=this.nsp)return;switch(packet.type){case parser.CONNECT:this.onconnect();break;case parser.EVENT:this.onevent(packet);break;case parser.BINARY_EVENT:this.onevent(packet);break;case parser.ACK:this.onack(packet);break;case parser.BINARY_ACK:this.onack(packet);break;case parser.DISCONNECT:this.ondisconnect();break;case parser.ERROR:this.emit("error",packet.data);break}};Socket.prototype.onevent=function(packet){var args=packet.data||[];debug("emitting event %j",args);if(null!=packet.id){debug("attaching ack callback to event");args.push(this.ack(packet.id))}if(this.connected){emit.apply(this,args)}else{this.receiveBuffer.push(args)}};Socket.prototype.ack=function(id){var self=this;var sent=false;return function(){if(sent)return;sent=true;var args=toArray(arguments);debug("sending ack %j",args);var type=hasBin(args)?parser.BINARY_ACK:parser.ACK;self.packet({type:type,id:id,data:args})}};Socket.prototype.onack=function(packet){var ack=this.acks[packet.id];if("function"==typeof ack){debug("calling ack %s with %j",packet.id,packet.data);ack.apply(this,packet.data);delete this.acks[packet.id]}else{debug("bad ack %s",packet.id)}};Socket.prototype.onconnect=function(){this.connected=true;this.disconnected=false;this.emit("connect");this.emitBuffered()};Socket.prototype.emitBuffered=function(){var i;for(i=0;i0&&opts.jitter<=1?opts.jitter:0;this.attempts=0}Backoff.prototype.duration=function(){var ms=this.ms*Math.pow(this.factor,this.attempts++);if(this.jitter){var rand=Math.random();var deviation=Math.floor(rand*this.jitter*ms);ms=(Math.floor(rand*10)&1)==0?ms-deviation:ms+deviation}return Math.min(ms,this.max)|0};Backoff.prototype.reset=function(){this.attempts=0};Backoff.prototype.setMin=function(min){this.ms=min};Backoff.prototype.setMax=function(max){this.max=max};Backoff.prototype.setJitter=function(jitter){this.jitter=jitter}},{}],37:[function(_dereq_,module,exports){var slice=[].slice;module.exports=function(obj,fn){if("string"==typeof fn)fn=obj[fn];if("function"!=typeof fn)throw new Error("bind() requires a function");var args=slice.call(arguments,2);return function(){return fn.apply(obj,args.concat(slice.call(arguments)))}}},{}],38:[function(_dereq_,module,exports){module.exports=Emitter;function Emitter(obj){if(obj)return mixin(obj)}function mixin(obj){for(var key in Emitter.prototype){obj[key]=Emitter.prototype[key]}return obj}Emitter.prototype.on=Emitter.prototype.addEventListener=function(event,fn){this._callbacks=this._callbacks||{};(this._callbacks["$"+event]=this._callbacks["$"+event]||[]).push(fn);return this};Emitter.prototype.once=function(event,fn){function on(){this.off(event,on);fn.apply(this,arguments)}on.fn=fn;this.on(event,on);return this};Emitter.prototype.off=Emitter.prototype.removeListener=Emitter.prototype.removeAllListeners=Emitter.prototype.removeEventListener=function(event,fn){this._callbacks=this._callbacks||{};if(0==arguments.length){this._callbacks={};return this}var callbacks=this._callbacks["$"+event];if(!callbacks)return this;if(1==arguments.length){delete this._callbacks["$"+event];return this}var cb;for(var i=0;i1)))/4)-floor((year-1901+month)/100)+floor((year-1601+month)/400)}}if(!(isProperty=objectProto.hasOwnProperty)){isProperty=function(property){var members={},constructor;if((members.__proto__=null,members.__proto__={toString:1},members).toString!=getClass){isProperty=function(property){var original=this.__proto__,result=property in(this.__proto__=null,this);this.__proto__=original;return result}}else{constructor=members.constructor;isProperty=function(property){var parent=(this.constructor||constructor).prototype;return property in this&&!(property in parent&&this[property]===parent[property])}}members=null;return isProperty.call(this,property)}}forEach=function(object,callback){var size=0,Properties,members,property;(Properties=function(){this.valueOf=0}).prototype.valueOf=0;members=new Properties;for(property in members){if(isProperty.call(members,property)){size++}}Properties=members=null;if(!size){members=["valueOf","toString","toLocaleString","propertyIsEnumerable","isPrototypeOf","hasOwnProperty","constructor"];forEach=function(object,callback){var isFunction=getClass.call(object)==functionClass,property,length;var hasProperty=!isFunction&&typeof object.constructor!="function"&&objectTypes[typeof object.hasOwnProperty]&&object.hasOwnProperty||isProperty;for(property in object){if(!(isFunction&&property=="prototype")&&hasProperty.call(object,property)){callback(property)}}for(length=members.length;property=members[--length];hasProperty.call(object,property)&&callback(property));}}else if(size==2){forEach=function(object,callback){var members={},isFunction=getClass.call(object)==functionClass,property;for(property in object){if(!(isFunction&&property=="prototype")&&!isProperty.call(members,property)&&(members[property]=1)&&isProperty.call(object,property)){callback(property)}}}}else{forEach=function(object,callback){var isFunction=getClass.call(object)==functionClass,property,isConstructor;for(property in object){if(!(isFunction&&property=="prototype")&&isProperty.call(object,property)&&!(isConstructor=property==="constructor")){callback(property)}}if(isConstructor||isProperty.call(object,property="constructor")){callback(property)}}}return forEach(object,callback)};if(!has("json-stringify")){var Escapes={92:"\\\\",34:'\\"',8:"\\b",12:"\\f",10:"\\n",13:"\\r",9:"\\t"};var leadingZeroes="000000";var toPaddedString=function(width,value){return(leadingZeroes+(value||0)).slice(-width)};var unicodePrefix="\\u00";var quote=function(value){var result='"',index=0,length=value.length,useCharIndex=!charIndexBuggy||length>10;var symbols=useCharIndex&&(charIndexBuggy?value.split(""):value);for(;index-1/0&&value<1/0){if(getDay){date=floor(value/864e5);for(year=floor(date/365.2425)+1970-1;getDay(year+1,0)<=date;year++);for(month=floor((date-getDay(year,0))/30.42);getDay(year,month+1)<=date;month++);date=1+date-getDay(year,month);time=(value%864e5+864e5)%864e5;hours=floor(time/36e5)%24;minutes=floor(time/6e4)%60;seconds=floor(time/1e3)%60;milliseconds=time%1e3}else{year=value.getUTCFullYear();month=value.getUTCMonth();date=value.getUTCDate();hours=value.getUTCHours();minutes=value.getUTCMinutes();seconds=value.getUTCSeconds();milliseconds=value.getUTCMilliseconds()}value=(year<=0||year>=1e4?(year<0?"-":"+")+toPaddedString(6,year<0?-year:year):toPaddedString(4,year))+"-"+toPaddedString(2,month+1)+"-"+toPaddedString(2,date)+"T"+toPaddedString(2,hours)+":"+toPaddedString(2,minutes)+":"+toPaddedString(2,seconds)+"."+toPaddedString(3,milliseconds)+"Z"}else{value=null}}else if(typeof value.toJSON=="function"&&(className!=numberClass&&className!=stringClass&&className!=arrayClass||isProperty.call(value,"toJSON"))){value=value.toJSON(property)}}if(callback){value=callback.call(object,property,value)}if(value===null){return"null"}className=getClass.call(value);if(className==booleanClass){return""+value}else if(className==numberClass){return value>-1/0&&value<1/0?""+value:"null"}else if(className==stringClass){return quote(""+value)}if(typeof value=="object"){for(length=stack.length;length--;){if(stack[length]===value){throw TypeError()}}stack.push(value);results=[];prefix=indentation;indentation+=whitespace;if(className==arrayClass){for(index=0,length=value.length;index0){for(whitespace="",width>10&&(width=10);whitespace.length=48&&charCode<=57||charCode>=97&&charCode<=102||charCode>=65&&charCode<=70)){abort()}}value+=fromCharCode("0x"+source.slice(begin,Index));break;default:abort()}}else{if(charCode==34){break}charCode=source.charCodeAt(Index);begin=Index;while(charCode>=32&&charCode!=92&&charCode!=34){charCode=source.charCodeAt(++Index)}value+=source.slice(begin,Index)}}if(source.charCodeAt(Index)==34){Index++;return value}abort();default:begin=Index;if(charCode==45){isSigned=true;charCode=source.charCodeAt(++Index)}if(charCode>=48&&charCode<=57){if(charCode==48&&(charCode=source.charCodeAt(Index+1),charCode>=48&&charCode<=57)){abort()}isSigned=false;for(;Index=48&&charCode<=57);Index++);if(source.charCodeAt(Index)==46){position=++Index;for(;position=48&&charCode<=57);position++);if(position==Index){abort()}Index=position}charCode=source.charCodeAt(Index);if(charCode==101||charCode==69){charCode=source.charCodeAt(++Index);if(charCode==43||charCode==45){Index++}for(position=Index;position=48&&charCode<=57);position++);if(position==Index){abort()}Index=position}return+source.slice(begin,Index)}if(isSigned){abort()}if(source.slice(Index,Index+4)=="true"){Index+=4;return true}else if(source.slice(Index,Index+5)=="false"){Index+=5;return false}else if(source.slice(Index,Index+4)=="null"){Index+=4;return null}abort()}}return"$"};var get=function(value){var results,hasMembers;if(value=="$"){abort()}if(typeof value=="string"){if((charIndexBuggy?value.charAt(0):value[0])=="@"){return value.slice(1)}if(value=="["){results=[];for(;;hasMembers||(hasMembers=true)){value=lex();if(value=="]"){break}if(hasMembers){if(value==","){value=lex();if(value=="]"){abort()}}else{abort()}}if(value==","){abort()}results.push(get(value))}return results}else if(value=="{"){results={};for(;;hasMembers||(hasMembers=true)){value=lex();if(value=="}"){break}if(hasMembers){if(value==","){value=lex();if(value=="}"){abort()}}else{abort()}}if(value==","||typeof value!="string"||(charIndexBuggy?value.charAt(0):value[0])!="@"||lex()!=":"){abort()}results[value.slice(1)]=get(lex()) +}return results}abort()}return value};var update=function(source,property,callback){var element=walk(source,property,callback);if(element===undef){delete source[property]}else{source[property]=element}};var walk=function(source,property,callback){var value=source[property],length;if(typeof value=="object"&&value){if(getClass.call(value)==arrayClass){for(length=value.length;length--;){update(value,length,callback)}}else{forEach(value,function(property){update(value,property,callback)})}}return callback.call(source,property,value)};exports.parse=function(source,callback){var result,value;Index=0;Source=""+source;result=get(lex());if(lex()!="$"){abort()}Index=Source=null;return callback&&getClass.call(callback)==functionClass?walk((value={},value[""]=result,value),"",callback):result}}}exports["runInContext"]=runInContext;return exports}if(freeExports&&!isLoader){runInContext(root,freeExports)}else{var nativeJSON=root.JSON,previousJSON=root["JSON3"],isRestored=false;var JSON3=runInContext(root,root["JSON3"]={noConflict:function(){if(!isRestored){isRestored=true;root.JSON=nativeJSON;root["JSON3"]=previousJSON;nativeJSON=previousJSON=null}return JSON3}});root.JSON={parse:JSON3.parse,stringify:JSON3.stringify}}if(isLoader){define(function(){return JSON3})}}).call(this)}).call(this,typeof self!=="undefined"?self:typeof window!=="undefined"?window:typeof global!=="undefined"?global:{})},{}],51:[function(_dereq_,module,exports){module.exports=toArray;function toArray(list,index){var array=[];index=index||0;for(var i=index||0;i") + so.Emit("sys", "Enter a name to connect as (or name:pass for a registered nick)") + + doneChan := make(chan struct{}, 0) + + so.On("hello", func(data map[string]string) { + log.Printf("[%s] Connection request", so.Id()) + go this.HubWorker(data["nick"], data["pass"], so, doneChan) + }) + + so.On("disconnection", func() { // n.b. not 'disconnect' (??) + log.Printf("[%s] Client dropped", so.Id()) + close(doneChan) + }) + +} + +func (this *App) ConfigRequestHandler(w http.ResponseWriter, r *http.Request) { + + confStruct := struct { + Extern string `json:"extern"` + Title string `json:"title"` + }{ + Extern: this.cfg.Web.Extern, + Title: this.cfg.Web.Title, + } + confBytes, _ := json.Marshal(confStruct) + + // + + w.Header().Set("Content-Type", "text/javascript") + w.WriteHeader(200) + fmt.Fprintf(w, "var DCWEBUI_CONF = %s;\n", string(confBytes)) +} + +func (this *App) StaticRequestHandler(w http.ResponseWriter, r *http.Request) { + fileName := r.URL.Path[1:] + + data, err := Asset(fileName) + if err != nil { + w.WriteHeader(404) + return + } + + knownContentTypes := map[string]string{ + ".htm": "text/html", + ".css": "text/css", + ".js": "application/javascript", + ".png": "image/png", + } + + foundMime := false + for ext, mimeType := range knownContentTypes { + if strings.HasSuffix(fileName, ext) { + w.Header().Set("Content-Type", mimeType) + foundMime = true + break + } + } + if !foundMime { + w.Header().Set("Content-Type", "application/x-octet-stream") + } + dataInfo, _ := AssetInfo(fileName) + w.Header().Set("Content-Length", fmt.Sprintf("%d", dataInfo.Size())) + + w.Write(data) +} + +func (this *App) RunServer() { + + // Socket.io handler + server, err := socketio.NewServer(nil) + if err != nil { + log.Fatal(err) + } + server.On("connection", this.SocketIOServer) + server.On("error", func(so socketio.Socket, err error) { + log.Println("error:", err) + }) + http.Handle("/socket.io/", server) + + // Configuration handler + http.HandleFunc("/conf", this.ConfigRequestHandler) + + // Other files: asset handler + http.HandleFunc("/", this.StaticRequestHandler) + + // Listen and serve + bindAddr := fmt.Sprintf("%s:%d", this.cfg.Web.BindTo, this.cfg.Web.Port) + log.Printf("Serving at %s...", bindAddr) + log.Fatal(http.ListenAndServe(bindAddr, nil)) +} + +func main() { + + a, err := NewApp("nmdc-webfrontend.conf") + if err != nil { + log.Fatal(err.Error()) + return + } + + a.RunServer() +} diff --git a/nmdc-webfrontend.conf b/nmdc-webfrontend.conf new file mode 100644 index 0000000..61cee40 --- /dev/null +++ b/nmdc-webfrontend.conf @@ -0,0 +1,23 @@ +{ + + "app": { + "name" : "DCWebUI2", + "version": "1.3.0", + "motd" : "Welcome!
    ", + "debug" : true + }, + + "web": { + "port" : 8082, + "bind_to": "127.0.0.1", + "extern" : "http://127.0.0.1:8082", + "title" : "DCWebUI" + }, + + "hub": { + "address": "127.0.0.1", + "port" : 411, + "tag" : "DCWebUI2.js" + } + +} \ No newline at end of file