Merge branch 'master' into room-access-restrictions

# Conflicts:
#	chatcommands.go
#	common/chatcommands.go
#	connection.go
#	static/css/site.css
#	static/main.html
This commit is contained in:
Zorchenhimer 2019-03-23 17:03:28 -04:00
commit 7448876299
10 changed files with 257 additions and 127 deletions

View File

@ -26,7 +26,9 @@ var commands = &CommandControl{
common.CNMe.String(): Command{ common.CNMe.String(): Command{
HelpText: "Display an action message.", HelpText: "Display an action message.",
Function: func(client *Client, args []string) string { Function: func(client *Client, args []string) string {
client.Me(strings.Join(args, " ")) if len(args) != 0 {
client.Me(strings.Join(args, " "))
}
return "" return ""
}, },
}, },
@ -139,7 +141,7 @@ var commands = &CommandControl{
// Clear/hide title if sent with no arguments. // Clear/hide title if sent with no arguments.
if len(args) == 0 { if len(args) == 0 {
cl.belongsTo.ClearPlaying() cl.belongsTo.ClearPlaying()
return "" return "Title cleared"
} }
link := "" link := ""
title := "" title := ""

View File

@ -48,7 +48,7 @@ func newChatRoom() (*ChatRoom, error) {
fmt.Printf("Loaded %d emotes\n", num) fmt.Printf("Loaded %d emotes\n", num)
//the "heartbeat" for broadcasting messages //the "heartbeat" for broadcasting messages
go cr.BroadCast() go cr.Broadcast()
return cr, nil return cr, nil
} }
@ -96,6 +96,7 @@ func (cr *ChatRoom) Join(name, uid string) (*Client, error) {
} }
} }
conn.clientName = name
client := &Client{ client := &Client{
name: name, name: name,
conn: conn, conn: conn,
@ -123,8 +124,8 @@ func (cr *ChatRoom) Join(name, uid string) (*Client, error) {
return client, nil return client, nil
} }
// TODO: fix this up a bit. kick and leave are the same, incorrect, error: "That name was already used!" // TODO: fix this up a bit. kick and leave are the same, incorrect, error: "That
//leaving the chatroom // name was already used!" leaving the chatroom
func (cr *ChatRoom) Leave(name, color string) { func (cr *ChatRoom) Leave(name, color string) {
defer cr.clientsMtx.Unlock() defer cr.clientsMtx.Unlock()
cr.clientsMtx.Lock() //preventing simultaneous access to the `clients` map cr.clientsMtx.Lock() //preventing simultaneous access to the `clients` map
@ -307,7 +308,7 @@ func (cr *ChatRoom) UserCount() int {
} }
//broadcasting all the messages in the queue in one block //broadcasting all the messages in the queue in one block
func (cr *ChatRoom) BroadCast() { func (cr *ChatRoom) Broadcast() {
send := func(data common.ChatData, client *Client) { send := func(data common.ChatData, client *Client) {
err := client.SendChatData(data) err := client.SendChatData(data)
if err != nil { if err != nil {
@ -320,20 +321,24 @@ func (cr *ChatRoom) BroadCast() {
case msg := <-cr.queue: case msg := <-cr.queue:
cr.clientsMtx.Lock() cr.clientsMtx.Lock()
for _, client := range cr.clients { for _, client := range cr.clients {
send(msg, client) go send(msg, client)
} }
for _, conn := range cr.tempConn {
data, err := msg.ToJSON() data, err := msg.ToJSON()
if err != nil { if err != nil {
fmt.Printf("Error converting ChatData to ChatDataJSON: %v\n", err) fmt.Printf("Error converting ChatData to ChatDataJSON: %v\n", err)
// Break out early because if one conversion fails, they all will fail } else {
break for uuid, conn := range cr.tempConn {
} go func(c *chatConnection, suid string) {
err = conn.WriteData(data) err = c.WriteData(data)
if err != nil { if err != nil {
fmt.Printf("Error writing data to connection: %v\n", err) fmt.Printf("Error writing data to connection: %v\n", err)
delete(cr.tempConn, suid)
}
}(conn, uuid)
} }
} }
cr.clientsMtx.Unlock() cr.clientsMtx.Unlock()
case msg := <-cr.modqueue: case msg := <-cr.modqueue:
cr.clientsMtx.Lock() cr.clientsMtx.Lock()

View File

@ -3,50 +3,72 @@ package common
import ( import (
"regexp" "regexp"
"strings" "strings"
"github.com/lucasb-eyer/go-colorful"
) )
// the values in colors must be lowercase so it matches with the color input
// this saves from having to call strings.ToLower(color) every time to check
var colors = []string{ var colors = []string{
"AliceBlue", "AntiqueWhite", "Aqua", "Aquamarine", "Azure", "aliceblue", "antiquewhite", "aqua", "aquamarine", "azure",
"Beige", "Bisque", "Black", "BlanchedAlmond", "Blue", "beige", "bisque", "black", "blanchedalmond", "blue",
"BlueViolet", "Brown", "BurlyWood", "CadetBlue", "Chartreuse", "blueviolet", "brown", "burlywood", "cadetblue", "chartreuse",
"Chocolate", "Coral", "CornflowerBlue", "Cornsilk", "Crimson", "chocolate", "coral", "cornflowerblue", "cornsilk", "crimson",
"Cyan", "DarkBlue", "DarkCyan", "DarkGoldenRod", "DarkGray", "cyan", "darkblue", "darkcyan", "darkgoldenrod", "darkgray",
"DarkGrey", "DarkGreen", "DarkKhaki", "DarkMagenta", "DarkOliveGreen", "darkgrey", "darkgreen", "darkkhaki", "darkmagenta", "darkolivegreen",
"DarkOrange", "DarkOrchid", "DarkRed", "DarkSalmon", "DarkSeaGreen", "darkorange", "darkorchid", "darkred", "darksalmon", "darkseagreen",
"DarkSlateBlue", "DarkSlateGray", "DarkSlateGrey", "DarkTurquoise", "DarkViolet", "darkslateblue", "darkslategray", "darkslategrey", "darkturquoise", "darkviolet",
"DeepPink", "DeepSkyBlue", "DimGray", "DimGrey", "DodgerBlue", "deeppink", "deepskyblue", "dimgray", "dimgrey", "dodgerblue",
"FireBrick", "FloralWhite", "ForestGreen", "Fuchsia", "Gainsboro", "firebrick", "floralwhite", "forestgreen", "fuchsia", "gainsboro",
"GhostWhite", "Gold", "GoldenRod", "Gray", "Grey", "ghostwhite", "gold", "goldenrod", "gray", "grey",
"Green", "GreenYellow", "HoneyDew", "HotPink", "IndianRed", "green", "greenyellow", "honeydew", "hotpink", "indianred",
"Indigo", "Ivory", "Khaki", "Lavender", "LavenderBlush", "indigo", "ivory", "khaki", "lavender", "lavenderblush",
"LawnGreen", "LemonChiffon", "LightBlue", "LightCoral", "LightCyan", "lawngreen", "lemonchiffon", "lightblue", "lightcoral", "lightcyan",
"LightGoldenRodYellow", "LightGray", "LightGrey", "LightGreen", "LightPink", "lightgoldenrodyellow", "lightgray", "lightgrey", "lightgreen", "lightpink",
"LightSalmon", "LightSeaGreen", "LightSkyBlue", "LightSlateGray", "LightSlateGrey", "lightsalmon", "lightseagreen", "lightskyblue", "lightslategray", "lightslategrey",
"LightSteelBlue", "LightYellow", "Lime", "LimeGreen", "Linen", "lightsteelblue", "lightyellow", "lime", "limegreen", "linen",
"Magenta", "Maroon", "MediumAquaMarine", "MediumBlue", "MediumOrchid", "magenta", "maroon", "mediumaquamarine", "mediumblue", "mediumorchid",
"MediumPurple", "MediumSeaGreen", "MediumSlateBlue", "MediumSpringGreen", "MediumTurquoise", "mediumpurple", "mediumseagreen", "mediumslateblue", "mediumspringgreen", "mediumturquoise",
"MediumVioletRed", "MidnightBlue", "MintCream", "MistyRose", "Moccasin", "mediumvioletred", "midnightblue", "mintcream", "mistyrose", "moccasin",
"NavajoWhite", "Navy", "OldLace", "Olive", "OliveDrab", "navajowhite", "navy", "oldlace", "olive", "olivedrab",
"Orange", "OrangeRed", "Orchid", "PaleGoldenRod", "PaleGreen", "orange", "orangered", "orchid", "palegoldenrod", "palegreen",
"PaleTurquoise", "PaleVioletRed", "PapayaWhip", "PeachPuff", "Peru", "paleturquoise", "palevioletred", "papayawhip", "peachpuff", "peru",
"Pink", "Plum", "PowderBlue", "Purple", "RebeccaPurple", "pink", "plum", "powderblue", "purple", "rebeccapurple",
"Red", "RosyBrown", "RoyalBlue", "SaddleBrown", "Salmon", "red", "rosybrown", "royalblue", "saddlebrown", "salmon",
"SandyBrown", "SeaGreen", "SeaShell", "Sienna", "Silver", "sandybrown", "seagreen", "seashell", "sienna", "silver",
"SkyBlue", "SlateBlue", "SlateGray", "SlateGrey", "Snow", "skyblue", "slateblue", "slategray", "slategrey", "snow",
"SpringGreen", "SteelBlue", "Tan", "Teal", "Thistle", "springgreen", "steelblue", "tan", "teal", "thistle",
"Tomato", "Turquoise", "Violet", "Wheat", "White", "tomato", "turquoise", "violet", "wheat", "white",
"WhiteSmoke", "Yellow", "YellowGreen", "whitesmoke", "yellow", "yellowgreen",
} }
// IsValidColor takes a string s and compares it against a list of css color names. // IsValidColor takes a string s and compares it against a list of css color names.
// It also accepts hex codes in the form of #000 (RGB), to #00000000 (RRGGBBAA), with A // It also accepts hex codes in the form of #000 (RGB), to #00000000 (RRGGBBAA), with A
// being the alpha value // being the alpha value
func IsValidColor(s string) bool { func IsValidColor(s string) bool {
s = strings.ToLower(s)
for _, c := range colors { for _, c := range colors {
if strings.ToLower(c) == strings.ToLower(s) { if s == c {
return true return true
} }
} }
return regexp.MustCompile(`^#[0-9A-Fa-f]{6}$`).MatchString(s) if regexp.MustCompile(`^#([0-9A-Fa-f]{3}){1,2}$`).MatchString(s) {
c, err := colorful.Hex(s)
if err != nil {
return false
}
total := c.R + c.G + c.B
return total > 0.7 && c.B/total < 0.7
}
return false
}
// RandomColor returns a hex color code
func RandomColor() string {
var color colorful.Color
for !IsValidColor(color.Hex()) {
color = colorful.FastHappyColor()
}
return color.Hex()
} }

View File

@ -3,8 +3,6 @@ package common
// Misc utils // Misc utils
import ( import (
"fmt"
"math/rand"
"regexp" "regexp"
) )
@ -16,14 +14,3 @@ func IsValidName(name string) bool {
return 3 <= len(name) && len(name) <= 36 && return 3 <= len(name) && len(name) <= 36 &&
usernameRegex.MatchString(name) && !IsValidColor(name) usernameRegex.MatchString(name) && !IsValidColor(name)
} }
// RandomColor returns a hex color code
func RandomColor() string {
nums := []int32{}
for i := 0; i < 6; i++ {
nums = append(nums, rand.Int31n(15))
}
return fmt.Sprintf("#%X%X%X%X%X%X",
nums[0], nums[1], nums[2],
nums[3], nums[4], nums[5])
}

View File

@ -10,24 +10,30 @@ import (
type chatConnection struct { type chatConnection struct {
*websocket.Conn *websocket.Conn
mutex sync.Mutex mutex sync.RWMutex
forwardedFor string forwardedFor string
clientName string
} }
func (cc *chatConnection) ReadData(data interface{}) error { func (cc *chatConnection) ReadData(data interface{}) error {
defer cc.mutex.Unlock() cc.mutex.RLock()
cc.mutex.Lock() defer cc.mutex.RUnlock()
stats.msgInInc() stats.msgInInc()
return cc.ReadJSON(data) return cc.ReadJSON(data)
} }
func (cc *chatConnection) WriteData(data interface{}) error { func (cc *chatConnection) WriteData(data interface{}) error {
defer cc.mutex.Unlock()
cc.mutex.Lock() cc.mutex.Lock()
defer cc.mutex.Unlock()
stats.msgOutInc() stats.msgOutInc()
err := cc.WriteJSON(data) err := cc.WriteJSON(data)
if err != nil { if err != nil {
return fmt.Errorf("Error writing data to %s: %v", cc.Host(), err) if operr, ok := err.(*net.OpError); ok {
fmt.Println("OpError: " + operr.Err.Error())
}
return fmt.Errorf("Error writing data to %s %s: %v", cc.clientName, cc.Host(), err)
} }
return nil return nil
} }

View File

@ -56,12 +56,12 @@ func main() {
fmt.Println("Listen and serve ", addr) fmt.Println("Listen and serve ", addr)
go startServer() go startServer()
go startRmptServer() go startRmtpServer()
<-exit <-exit
} }
func startRmptServer() { func startRmtpServer() {
server := &rtmp.Server{ server := &rtmp.Server{
HandlePlay: handlePlay, HandlePlay: handlePlay,
HandlePublish: handlePublish, HandlePublish: handlePublish,

View File

@ -4,6 +4,7 @@
--var-message-color: #f4f4f4; --var-message-color: #f4f4f4;
--var-contrast-color: #1bf7ec; --var-contrast-color: #1bf7ec;
--var-background-color: #0F0F11; --var-background-color: #0F0F11;
--var-popout-color: #393940;
--var-max-height: 98vh; --var-max-height: 98vh;
--var-max-width: 98vw; --var-max-width: 98vw;
} }
@ -111,23 +112,30 @@ span.svmsg {
color: #B1B1B1; color: #B1B1B1;
} }
.access-grant { .contrast {
font-weight: normal; color: var(--var-contrast-color);
color: greenyellow;
} }
.access-ignore { .range-div {
font-weight: normal; margin-bottom: 5px;
color: red; display: flex;
justify-content: center;
align-items: center;
border-radius: 5px;
padding: 5px;
} }
.access-name { .range-div>input[type=button] {
color: #f7b11b; flex: 2;
} }
.access-title { .hiddendiv {
color: #e5e0e5; display: none;
font-weight: bold; color: var(--var-message-color);
background: var(--var-popout-color);
padding: 2em;
border-bottom-left-radius: 5px;
border-bottom-right-radius: 5px;
} }
#videoElement { #videoElement {
@ -154,7 +162,7 @@ span.svmsg {
right: 0px; right: 0px;
margin: 0px 5px; margin: 0px 5px;
padding: 3px; padding: 3px;
background: #393940; background: var(--var-popout-color);
} }
#hidden:hover, #hidden:hover,
@ -262,4 +270,8 @@ span.svmsg {
#helpbody { #helpbody {
color: #b1b1b1; color: #b1b1b1;
}
#colorSubmit:disabled {
display: none;
} }

View File

@ -4,6 +4,9 @@ function setPlaying(title, link) {
if (title !== "") { if (title !== "") {
$('#playing').text(title); $('#playing').text(title);
document.title = "Movie Night | " + title; document.title = "Movie Night | " + title;
} else {
$('#playing').text("");
document.title = "Movie Night";
} }
$('#playing').removeAttr('href'); $('#playing').removeAttr('href');
@ -89,7 +92,11 @@ function join() {
} }
function websocketSend(data) { function websocketSend(data) {
ws.send(data); if (ws.readyState == ws.OPEN) {
ws.send(data);
} else {
console.log("did not send data because websocket is not open", data);
}
} }
function sendChat() { function sendChat() {
@ -126,13 +133,57 @@ function help() {
sendMessage("/help"); sendMessage("/help");
} }
function showColors(show) {
$("#hiddencolor").css("display", show ? "block" : "");
}
function colorAsHex() {
let r = parseInt($("#colorRed").val()).toString(16).padStart(2, "0");
let g = parseInt($("#colorGreen").val()).toString(16).padStart(2, "0");
let b = parseInt($("#colorBlue").val()).toString(16).padStart(2, "0");
return `#${r}${g}${b}`
}
function updateColor() {
let r = $("#colorRed").val();
let g = $("#colorGreen").val();
let b = $("#colorBlue").val();
$("#colorRedLabel").text(r.padStart(3, "0"));
$("#colorGreenLabel").text(g.padStart(3, "0"));
$("#colorBlueLabel").text(b.padStart(3, "0"));
$("#colorName").css("color", `rgb(${r}, ${g}, ${b})`);
if (isValidColor(colorAsHex())) {
$("#colorWarning").text("");
} else {
$("#colorWarning").text("Unreadable Color");
}
}
function changeColor() {
if (isValidColor(colorAsHex())) {
sendMessage("/color " + colorAsHex());
showColors(false);
}
}
// Get the websocket setup in a function so it can be recalled // Get the websocket setup in a function so it can be recalled
function setupWebSocket() { function setupWebSocket() {
ws = new WebSocket(getWsUri()); ws = new WebSocket(getWsUri());
ws.onmessage = (m) => recieveMessage(m.data); ws.onmessage = (m) => recieveMessage(m.data);
ws.onopen = (e) => console.log("Websocket Open:", e); ws.onopen = () => console.log("Websocket Open");
ws.onclose = () => closeChat(); ws.onclose = () => {
ws.onerror = (e) => console.log("Websocket Error:", e); closeChat();
setNotifyBox("The connection to the server has closed. Please refresh page to connect again.");
$("#joinbox").css("display", "none");
}
ws.onerror = (e) => {
console.log("Websocket Error:", e);
e.target.close();
}
} }
function setupEvents() { function setupEvents() {
@ -159,10 +210,6 @@ function setupEvents() {
input: () => processMessage(), input: () => processMessage(),
}); });
$("#hiddenColorPicker").on({
change: () => sendMessage("/color " + $("#hiddenColorPicker").val()),
});
$("#send").on({ $("#send").on({
click: () => $("#msg").focus(), click: () => $("#msg").focus(),
}); });
@ -172,6 +219,12 @@ function setupEvents() {
).observe($("#suggestions")[0], { childList: true }); ).observe($("#suggestions")[0], { childList: true });
} }
function defaultValues() {
$("#colorRed").val(0).trigger("input");
$("#colorGreen").val(0).trigger("input");
$("#colorBlue").val(0).trigger("input");
}
window.addEventListener("onresize", updateSuggestionCss); window.addEventListener("onresize", updateSuggestionCss);
window.addEventListener("load", () => { window.addEventListener("load", () => {
@ -179,7 +232,42 @@ window.addEventListener("load", () => {
setupWebSocket(); setupWebSocket();
startGo(); startGo();
setupEvents(); setupEvents();
defaultValues();
// Make sure name is focused on start // Make sure name is focused on start
$("#name").focus(); $("#name").focus();
}); });
function pleaseremovethis() {
colors = ["aliceblue", "antiquewhite", "aqua", "aquamarine", "azure",
"beige", "bisque", "black", "blanchedalmond", "blue",
"blueviolet", "brown", "burlywood", "cadetblue", "chartreuse",
"chocolate", "coral", "cornflowerblue", "cornsilk", "crimson",
"cyan", "darkblue", "darkcyan", "darkgoldenrod", "darkgray",
"darkgrey", "darkgreen", "darkkhaki", "darkmagenta", "darkolivegreen",
"darkorange", "darkorchid", "darkred", "darksalmon", "darkseagreen",
"darkslateblue", "darkslategray", "darkslategrey", "darkturquoise", "darkviolet",
"deeppink", "deepskyblue", "dimgray", "dimgrey", "dodgerblue",
"firebrick", "floralwhite", "forestgreen", "fuchsia", "gainsboro",
"ghostwhite", "gold", "goldenrod", "gray", "grey",
"green", "greenyellow", "honeydew", "hotpink", "indianred",
"indigo", "ivory", "khaki", "lavender", "lavenderblush",
"lawngreen", "lemonchiffon", "lightblue", "lightcoral", "lightcyan",
"lightgoldenrodyellow", "lightgray", "lightgrey", "lightgreen", "lightpink",
"lightsalmon", "lightseagreen", "lightskyblue", "lightslategray", "lightslategrey",
"lightsteelblue", "lightyellow", "lime", "limegreen", "linen",
"magenta", "maroon", "mediumaquamarine", "mediumblue", "mediumorchid",
"mediumpurple", "mediumseagreen", "mediumslateblue", "mediumspringgreen", "mediumturquoise",
"mediumvioletred", "midnightblue", "mintcream", "mistyrose", "moccasin",
"navajowhite", "navy", "oldlace", "olive", "olivedrab",
"orange", "orangered", "orchid", "palegoldenrod", "palegreen",
"paleturquoise", "palevioletred", "papayawhip", "peachpuff", "peru",
"pink", "plum", "powderblue", "purple", "rebeccapurple",
"red", "rosybrown", "royalblue", "saddlebrown", "salmon",
"sandybrown", "seagreen", "seashell", "sienna", "silver",
"skyblue", "slateblue", "slategray", "slategrey", "snow",
"springgreen", "steelblue", "tan", "teal", "thistle",
"tomato", "turquoise", "violet", "wheat", "white",
"whitesmoke", "yellow", "yellowgreen",]
}

View File

@ -1,80 +1,84 @@
{{define "header"}} {{define "header"}}
{{ if .Chat }} {{if .Chat}}
<script type="application/javascript" src="/static/js/wasm_exec.js"></script> <script type="application/javascript" src="/static/js/wasm_exec.js"></script>
<script type="application/javascript" src="/static/js/chat.js"></script> <script type="application/javascript" src="/static/js/chat.js"></script>
<script> <script>
maxMessageCount = {{ .MessageHistoryCount }} maxMessageCount = {{.MessageHistoryCount }}
</script> </script>
<style> <style>
.root { .root {
display: grid; display: grid;
} }
</style> </style>
{{ end }} {{end}}
{{ if .Video }} {{if .Video}}
<script type="application/javascript" src="/static/js/flv.min.js"></script> <script type="application/javascript" src="/static/js/flv.min.js"></script>
<script type="application/javascript" src="/static/js/video.js"></script> <script type="application/javascript" src="/static/js/video.js"></script>
{{ if not .Chat }} {{if not .Chat}}
<style> <style>
#videoElement { #videoElement {
height: 99vh; height: 99vh;
} }
</style> </style>
{{ end }} {{end}}
{{ end }} {{end}}
{{ if and .Video .Chat }} {{if and .Video .Chat}}
<style> <style>
.root { .root {
grid-template-columns: 5fr 1fr; grid-template-columns: 5fr 1fr;
} }
</style> </style>
{{ end }} {{end}}
{{end}} {{end}}
{{define "body"}} {{define "body"}}
{{ if .Video }} {{if .Video}}
<video id="videoElement" controls autoplay x5-video-player-type="h5" x5-video-player-fullscreen="true" playsinline <video id="videoElement" controls autoplay x5-video-player-type="h5" x5-video-player-fullscreen="true" playsinline
webkit-playsinline> webkit-playsinline>
Your browser is too old and doesn't support HTML5 video. Your browser is too old and doesn't support HTML5 video.
</video> </video>
{{ end }} {{end}}
{{ if .Chat }} {{if .Chat}}
<div id="chatwindow"> <div id="chatwindow">
<div id="notifyBox"></div> <div id="notifyBox"></div>
<div id="chat" style="display: none;"> <div id="chat" style="display: none;">
<input id="hiddenColorPicker" type="color" hidden />
<div id="optionBox">Please hover to view options</div> <div id="optionBox">Please hover to view options</div>
<div id="hidden"> <div id="hidden">
<div id="chatButtons"> <div id="chatButtons">
<button class="button pretty-button" onclick="auth();">Auth</button> <input type="button" class="button pretty-button" onclick="auth();" value="Auth" />
<button class="button pretty-button" onclick="help();">Help</button> <input type="button" class="button pretty-button" onclick="help();" value="Help" />
<button class="button pretty-button" onclick="$('#hiddenColorPicker').trigger('click');">Color</button> <input type="button" class="button pretty-button" onclick="showColors(true);" value="Color" />
<button class="button pretty-button" onclick="nick();">Nick</button> <input type="button" class="button pretty-button" onclick="nick();" value="Nick" />
{{ if .Video }} {{if .Video}}
<button class="button pretty-button" onclick="initPlayer();">Reload Player</button> <input type="button" class="button pretty-button" onclick="initPlayer();" value="Reload Player" />
{{ end }} {{end}}
</div> </div>
<hr /> <hr />
<div id="accessRequest"> <div id="hiddencolor" class="hiddendiv">
<div class="access-title" noclear>Grant Access?</div> <div class="range-div" style="background-image: linear-gradient(to right, transparent, red);">
<div> <input id="colorRed" type="range" min="0" max="255" value="0" oninput="updateColor();" />
<button class="button pretty-button access-grant"></button> <span id="colorRedLabel"></span>
<button class="button pretty-button access-ignore"></button>
<span class="access-name">User1</span>
</div> </div>
<div> <div class="range-div" style="background-image: linear-gradient(to right, transparent, green);">
<button class="button pretty-button access-grant"></button> <input id="colorGreen" type="range" min="0" max="255" value="0" oninput="updateColor();" />
<button class="button pretty-button access-ignore"></button> <span id="colorGreenLabel"></span>
<span class="access-name">User2</span>
</div> </div>
<div> <div class="range-div" style="background-image: linear-gradient(to right, transparent, blue);">
<button class="button pretty-button access-grant"></button> <input id="colorBlue" type="range" min="0" max="255" value="0" oninput="updateColor();" />
<button class="button pretty-button access-ignore"></button> <span id="colorBlueLabel"></span>
<span class="access-name">User3</span>
</div> </div>
<div id="colorName" class="range-div" style="font-weight: bold;">
NAME
</div>
<div id="colorWarning" class="range-div contrast">
</div>
<dvi class="range-div">
<input id="colorSubmit" type="button" class="button pretty-button" value="Select"
onclick="changeColor();" />
</dvi>
</div> </div>
</div> </div>
<a id="playing" target="_blank"></a> <a id="playing" target="_blank"></a>
@ -83,7 +87,7 @@
<div id="suggestions"></div> <div id="suggestions"></div>
<textarea id="msg"></textarea> <textarea id="msg"></textarea>
</div> </div>
<button id="send" class="button" onclick="sendChat();">Send</button> <input id="send" type="button" class="button" onclick="sendChat();" value="Send" />
<div> <div>
<!-- This is an empty div so there can be an empty space below the send button --> <!-- This is an empty div so there can be an empty space below the send button -->
</div> </div>
@ -91,10 +95,10 @@
<div id="joinbox"> <div id="joinbox">
<div style="color: #e5e0e5; text-align: center;">Please enter your name<br />to join the chat</div> <div style="color: #e5e0e5; text-align: center;">Please enter your name<br />to join the chat</div>
<div> <div>
<input id="name" maxlength="36"> <input id="name" type="text" maxlength="36">
<button id="join" class="button pretty-button" onclick="join();">Join</button> <input id="join" type="button" class="button pretty-button" onclick="join();" value="Join" />
</div> </div>
</div> </div>
</div> </div>
{{ end }} {{end}}
{{end}} {{end}}

View File

@ -224,6 +224,10 @@ func recieve(v []js.Value) {
} }
func websocketSend(msg string, dataType common.ClientDataType) error { func websocketSend(msg string, dataType common.ClientDataType) error {
if strings.TrimSpace(msg) == "" {
return nil
}
data, err := json.Marshal(common.ClientData{ data, err := json.Marshal(common.ClientData{
Type: dataType, Type: dataType,
Message: msg, Message: msg,