20 Commits

Author SHA1 Message Date
1c7842c182 readme 2017-02-11 13:13:09 +13:00
9e33e50986 display ip addresses 2017-02-09 19:28:43 +13:00
7f618db70a fix not displaying nonzero share sizes 2017-02-08 19:02:20 +13:00
7894355647 popups: only show when manually turning on the feature, not when it's auto-enabled on load 2017-02-08 18:34:12 +13:00
af324441b7 client: clicking the popup refocus browser window, switch to PM tab as necessary 2017-02-08 18:33:17 +13:00
d862a3f703 more consistent comment style for URL references 2017-02-08 18:20:35 +13:00
59c118dc34 remove queryselectorall, always use the fallback implementation 2017-02-08 18:19:57 +13:00
42a8298362 standardise event cancellation 2017-02-08 18:19:33 +13:00
c4e37bf47d explicit falsey checks for some persistent vars (no practical difference) 2017-02-06 16:49:37 +13:00
95d56dbca2 Added tag release-1.1.2 for changeset 7278eb0d067d 2017-02-06 16:44:27 +13:00
761d0bfad5 build: set build version in binary at build time 2017-02-06 16:43:17 +13:00
1d3f16e6c6 change default hub name in sample file 2017-02-06 16:43:07 +13:00
e5ceadb03a doc: changelog 2017-02-06 16:39:27 +13:00
6999069fd7 server: set SERVER header on all requests (even SIO ones), display version on startup 2017-02-06 16:35:25 +13:00
5da61d5922 whitespace in sample config file 2017-02-06 16:27:33 +13:00
d33ba5c085 remove 'extern' from config files 2017-02-06 16:27:14 +13:00
a28e5ce9b0 remove extra /conf/ request 2017-02-06 16:25:49 +13:00
3448cb7eeb don't require extern 2017-02-06 16:20:31 +13:00
ba941adfdd client: fix padding around user count in title 2017-02-06 13:54:24 +13:00
48d96f9efe Added tag release-1.1.1 for changeset d14041daa7bb 2017-02-06 13:46:20 +13:00
8 changed files with 112 additions and 107 deletions

View File

@@ -2,3 +2,5 @@
9ed95938d809a8226aca529e34b655e6d8c8c379 release-1.0.1
46fe533682419c8a519836ac95b5575053aa0fa8 release-1.0.2
a2c92b262f339f82eb01c8d92dda252a27432255 release-1.1.0
d14041daa7bbbd37ea2ff47aa978b9595af67ca3 release-1.1.1
7278eb0d067d8ed2a653de6a1feeeb7f76fb9891 release-1.1.2

View File

@@ -8,7 +8,6 @@ type Config struct {
Web struct {
Port int `json:"port"`
BindTo string `json:"bind_to"`
Extern string `json:"extern"`
Title string `json:"title"`
CustomFavicon bool `json:"custom_favicon"`

View File

@@ -8,10 +8,24 @@ Tags: nmdc
=UPGRADING FROM DCWEBUI2=
- The configuration file format is identical, but please now ensure it's valid json instead of just a .js file. This means no assignment, use double-quoted strings, and no comments.
- The configuration file content is identical between nmdc-webfrontend 1.0.0 and dcwebui2 1.3.0, but please now ensure it's valid JSON instead of arbitrary javascript. This means no assignment, use double-quoted strings, and no comments.
- Future changes to the configuration file since nmdc-webfrontend 1.0.0 are backward compatible (see the changelog for more details).
=CHANGELOG=
2017-02-11 1.1.3
- Feature: Display user IP address on hover, if available
- Enhancement: Allow clicking on popup notifications
- Enhancement: Only show 'popups enabled' notification when enabling for the first time, not persistent page load
- Update libnmdc to 0.13
- Fix a cosmetic issue with not displaying non-zero user share sizes
2017-02-06 1.1.2
- Autodetect 'extern', no need to include it in config files
- Enhancement: Remove redundant request, for a faster page load
- Display server version number in log file and in response headers
- Fix a cosmetic issue with spacing around user count in page title
2017-02-06 1.1.1
- Fix an issue with malformed content in minified build

View File

@@ -88,7 +88,7 @@ single_build() {
# GOARCH/GOOS supplied in function env
go build \
-a \
-ldflags '-s -w' \
-ldflags "-s -w -X main.VERSION=nmdc-webfrontend/${version}" \
-gcflags "-trimpath=${GOPATH}" \
-asmflags "-trimpath=${GOPATH}" \
-o "$(pathfix "${tmpdir}/${local_bin_name}")"

View File

@@ -5,24 +5,24 @@
var SENTINEL_PASSWORD = "************";
var CHAT_SCROLLBACK_LIMIT = 50; // Once over 2x $limit, the first $limit will be trimmed off the list
var EXTERN_ROOT = window.location.protocol + "//" + window.location.host + "/";
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 $ = function(s) {
// There used to be a querySelectorAll implementation, but, better that we don't have
// potentially-incompatible implementations if this one does actually work.
// i'm not writing a selector engine...
if (! s.length) {
return [];
}
);
if (s[0] === '#') {
return document.getElementById(s.slice(1)); // single element
} else if (s[0] === '.') {
return document.getElementsByClassName(s.slice(1)); // multiple elements
} else {
return document.getElementsByTagName(s); // multiple elements
}
};
var nmdc_escape = function(str) {
return (
@@ -46,8 +46,8 @@ var fmtBytes = function(b) {
var k = 1024;
var sizes = [' B', ' KiB', ' MiB', ' GiB', ' TiB'];
var i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(3)) + sizes[i];
var i = Math.floor(Math.log(b) / Math.log(k));
return parseFloat((b / Math.pow(k, i)).toFixed(3)) + sizes[i];
};
@@ -95,7 +95,7 @@ var b64 = function(str) {
})).replace(/=/g, '');
}
// https://gist.github.com/eligrey/1276030
// @ref https://gist.github.com/eligrey/1276030
var appendInnerHTML = function($el, html) {
var child = document.createElement("span");
child.innerHTML = html;
@@ -106,7 +106,7 @@ var appendInnerHTML = function($el, html) {
}
};
// http://stackoverflow.com/a/5598797
// @ref http://stackoverflow.com/a/5598797
function getOffsetLeft( elem ) {
var offsetLeft = 0;
do {
@@ -146,17 +146,23 @@ var date_format = function(d, format) {
/* */
var notify = function(title, body) {
var notify = function(title, body, tab) {
if (!("Notification" in window)) {
return; // not supported by browser
}
switch (window.Notification.permission) {
case "granted": {
new Notification(title, {
var n = new Notification(title, {
body: body,
icon: DCWEBUI_CONF.extern + "/favicon.ico"
icon: EXTERN_ROOT + "/favicon.ico"
});
n.onclick = function() {
parent.focus(); // recent chrome
window.focus(); // older browsers
tab_set(tab);
this.close();
};
} break;
case "denied": return;
@@ -242,8 +248,7 @@ var userMenu = function(u, ev) {
usermenu.show();
//
ev.preventDefault();
return false;
return noprop(ev);
};
var userlist = {
@@ -335,6 +340,9 @@ var userlist = {
if (props.ClientTag.length > 0) {
prop_str.push(props.ClientTag + " " + props.ClientVersion);
}
if (props.IPAddress.length > 0) {
prop_str.push(props.IPAddress);
}
prop_str.push("Sharing " + fmtBytes(props.ShareSize));
for (var i = 0; i < $el.length; ++i) {
@@ -364,7 +372,7 @@ var submit = function() {
}
if (hub_pass === SENTINEL_PASSWORD) {
// Probably not a real password. Attempt to load a better one from the saved state
var cache = persistence_get("login");
var cache = persistence_get("login", "");
if (cache.indexOf(":") != -1) {
hub_pass = cache.substr(cache.indexOf(":") + 1);
}
@@ -524,6 +532,20 @@ var tab_free = function(id) {
}
};
var noprop = function(ev) {
if (ev.preventDefault) {
ev.preventDefault();
}
if (ev.stopPropagation) {
ev.stopPropagation();
} else {
ev.cancelBubble = true; // oldIE
}
return false;
}
var tab_addHandlers = function() {
var tabitems = $(".tabitem");
for (var i = 0; i < tabitems.length; i++) {
@@ -532,13 +554,7 @@ var tab_addHandlers = function() {
tabitems[i].onclick = function(ev) {
tab_set( this.getAttribute('data-tab') );
// 360nobubble
if (ev.stopPropagation) {
ev.stopPropagation();
} else {
ev.cancelBubble = true; // oldIE
}
return false;
return noprop(ev);
};
}
@@ -549,13 +565,7 @@ var tab_addHandlers = function() {
tabclosers[i].onclick = function(ev) {
tab_free( this.getAttribute('data-tab') );
// 360nobubble
if (ev.stopPropagation) {
ev.stopPropagation();
} else {
ev.cancelBubble = true; // oldIE
}
return false;
return noprop(ev);
};
}
};
@@ -779,13 +789,13 @@ var updateTitle = function() {
prefix = "[NEW PM] "
}
document.title = prefix + hub_hubname + "("+userlist.count()+") "
document.title = prefix + hub_hubname + " ("+userlist.count()+")"
};
var sock = {};
var hub_state = 0; // [disconnected, sent-nick, connected]
var hub_last_nick = '';
var hub_hubname = DCWEBUI_CONF.title;
var hub_hubname = "Loading...";
var pm_tabs = {}; // nick => tabid
var next_tabid = 1;
@@ -847,7 +857,7 @@ var desktop_notifications_toggle = function(ev) {
persistence_set("notifications", desktop_notifications_enabled);
if (desktop_notifications_enabled) {
desktop_notifications_onEnable();
notify(hub_hubname, "Desktop popups enabled", "tab-main");
}
persistence_set("popups", desktop_notifications_enabled);
@@ -855,10 +865,6 @@ var desktop_notifications_toggle = function(ev) {
$el.innerHTML = desktop_notifications_fmtstr();
};
var desktop_notifications_onEnable = function() {
notify(hub_hubname, "Desktop popups enabled");
}
var scrollback_move = function(delta) {
if (chat_scrollback.length === 0) {
return; // no effect
@@ -917,7 +923,8 @@ var transition = function(new_state) {
$("#chatbox").disabled = true;
} break;
case STATE_ACTIVE: {
case STATE_ACTIVE: {
write("tab-main").system("Now talking on "+hub_hubname);
$("#chatbox").disabled = false;
$("#chatbox").spellcheck = true;
} break;
@@ -940,33 +947,28 @@ window.onload = function() {
show_joins = persistence_get("show_joins", false);
document.title = DCWEBUI_CONF.title;
document.title = hub_hubname; // "Loading...";
// HTML event handlers
$("#form-none").onsubmit = function(ev) {
submit();
// don't submit form
ev.preventDefault();
return false;
return noprop(ev); // don't submit form
};
$("#chatbox").onkeydown = function(ev) {
if (ev.keyCode === 9 /* Tab */) {
tabcompletion_start( ev.shiftKey ? -1 : 1 );
ev.preventDefault();
return false;
return noprop(ev);
} else if (ev.keyCode == 38 /* ArrowUp */ && ev.ctrlKey) {
scrollback_move(-1);
ev.preventDefault();
return false;
return noprop(ev);
} else if (ev.keyCode == 40 /* ArrowDown */ && ev.ctrlKey) {
scrollback_move(1);
ev.preventDefault();
return false;
return noprop(ev);
} else {
tabcompletion_inactive();
@@ -985,9 +987,7 @@ window.onload = function() {
$("#menubutton").onclick = function(ev) {
menu.toggle();
ev.preventDefault();
ev.stopPropagation();
return false;
return noprop(ev);
};
window.onclick = function() {
@@ -1007,13 +1007,9 @@ window.onload = function() {
timestamp_format_index = persistence_get("timestamps", 0);
should_warn_on_close = persistence_get("warnonclose");
should_warn_on_close = persistence_get("warnonclose", false);
desktop_notifications_enabled = persistence_get("popups");
if (desktop_notifications_enabled) {
// prompt for permissions
desktop_notifications_onEnable();
}
desktop_notifications_enabled = persistence_get("popups", false);
menu.reset();
@@ -1039,7 +1035,7 @@ window.onload = function() {
// Socket event handlers
sock = io.connect(DCWEBUI_CONF.extern);
sock = io.connect(EXTERN_ROOT);
sock.on('cls', function() {
transition(STATE_READY_FOR_LOGIN);
@@ -1062,7 +1058,6 @@ window.onload = function() {
}
});
sock.on('hubname', function(s) {
write("tab-main").system("Now talking on "+s);
hub_hubname = s;
updateTitle();
});
@@ -1089,7 +1084,7 @@ window.onload = function() {
tab_mark_unread( pm_tabs[data.user] );
if (desktop_notifications_enabled) {
notify("Message from " + data.user, data.message);
notify("Message from " + data.user, data.message, pm_tabs[data.user]);
}
}
});

View File

@@ -53,7 +53,6 @@
</div>
<script type="text/javascript" src="/socket.io-1.7.2.js"></script>
<script type="text/javascript" src="/conf"></script>
<script type="text/javascript" src="/dcwebui.js"></script>
</body>
</html>

51
main.go
View File

@@ -12,6 +12,8 @@ import (
"github.com/googollee/go-socket.io"
)
var VERSION string = `nmdc-webfrontend/devel-unreleased`
type App struct {
cfg *Config
}
@@ -164,6 +166,7 @@ func (this *App) SocketIOServer(so socketio.Socket) {
log.Printf("[%s] Client connected", so.Id())
so.Emit("cls")
so.Emit("hubname", this.cfg.Web.Title)
so.Emit("raw", this.cfg.App.MotdHTML+"<br>")
so.Emit("sys", "Enter a name to connect as (or name:pass for a registered nick)")
@@ -185,24 +188,6 @@ func (this *App) customFaviconHandler(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, "favicon.ico")
}
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:]
if fileName == "" {
@@ -241,6 +226,9 @@ func (this *App) StaticRequestHandler(w http.ResponseWriter, r *http.Request) {
func (this *App) RunServer() {
// Inner mux {{
innerMux := http.NewServeMux()
// Socket.io handler
server, err := socketio.NewServer(nil)
if err != nil {
@@ -250,30 +238,39 @@ func (this *App) RunServer() {
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)
innerMux.Handle("/socket.io/", server)
// Custom favicon handler
if this.cfg.Web.CustomFavicon {
http.HandleFunc("/favicon.ico", this.customFaviconHandler)
innerMux.HandleFunc("/favicon.ico", this.customFaviconHandler)
}
// Other files: asset handler
// Asset handler
if this.cfg.Web.ExternalWebroot {
http.Handle("/", http.FileServer(http.Dir("client")))
innerMux.Handle("/", http.FileServer(http.Dir("client")))
} else {
http.HandleFunc("/", this.StaticRequestHandler)
innerMux.HandleFunc("/", this.StaticRequestHandler)
}
// }}
// Wrapper mux {{
outerMux := http.NewServeMux()
outerMux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Server", VERSION)
innerMux.ServeHTTP(w, r)
})
// }}
// 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))
log.Fatal(http.ListenAndServe(bindAddr, outerMux))
}
func main() {
log.Println(VERSION)
a, err := NewApp("nmdc-webfrontend.conf")
if err != nil {
log.Fatal(err.Error())

View File

@@ -1,21 +1,20 @@
{
"app": {
"motd" : "Welcome!<br>"
"motd": "Welcome!<br>"
},
"web": {
"port" : 8082,
"port": 8082,
"bind_to": "127.0.0.1",
"extern" : "http://127.0.0.1:8082",
"title" : "DCWebUI",
"title": "NMDC Web Frontend",
"custom_favicon": false
},
"hub": {
"address": "127.0.0.1",
"port" : 411,
"tag" : "nmdc-webfrontend"
"port": 411,
"tag": "nmdc-webfrontend"
}
}