diff --git a/chatclient.go b/chatclient.go index a36fd0e..d8b2fe0 100644 --- a/chatclient.go +++ b/chatclient.go @@ -119,7 +119,7 @@ func (cl *Client) Send(s string) { // Send server message to this client func (cl *Client) ServerMessage(msg string) { msg = ParseEmotes(msg) - encoded, err := common.EncodeMessage("", "#ea6260", msg, common.MSG_ERROR) + encoded, err := common.EncodeMessage("", "#ea6260", msg, common.MsgError) if err != nil { fmt.Printf("Error encoding server message to %s: %s; Message: %s\n", cl.name, err, msg) return diff --git a/chatcommands.go b/chatcommands.go index 0995058..9818569 100644 --- a/chatcommands.go +++ b/chatcommands.go @@ -9,7 +9,6 @@ import ( "github.com/zorchenhimer/MovieNight/common" ) -var commands *CommandControl var colorRegex *regexp.Regexp = regexp.MustCompile(`^#[0-9A-Fa-f]{6}$`) type CommandControl struct { @@ -27,220 +26,216 @@ type CommandFunction func(client *Client, args []string) string //type HelpFunction func(client *Client) string -func init() { - commands = &CommandControl{ - user: map[string]Command{ - "me": Command{ - HelpText: "Display an action message.", - Function: func(client *Client, args []string) string { - client.Me(strings.Join(args, " ")) - return "" - }, +var commands = &CommandControl{ + user: map[string]Command{ + common.CNMe.String(): Command{ + HelpText: "Display an action message.", + Function: func(client *Client, args []string) string { + client.Me(strings.Join(args, " ")) + return "" }, + }, - "help": Command{ - HelpText: "This help text.", - Function: cmdHelp, - }, + common.CNHelp.String(): Command{ + HelpText: "This help text.", + Function: cmdHelp, + }, - "count": Command{ - HelpText: "Display number of users in chat.", - Function: func(client *Client, args []string) string { - return fmt.Sprintf("Users in chat: %d", client.belongsTo.UserCount()) - }, + common.CNCount.String(): Command{ + HelpText: "Display number of users in chat.", + Function: func(client *Client, args []string) string { + return fmt.Sprintf("Users in chat: %d", client.belongsTo.UserCount()) }, + }, - "color": cmdColor, - "colour": cmdColor, + common.CNColor.String(): cmdColor, - "w": cmdWhoAmI, - "whoami": cmdWhoAmI, + common.CNWhoAmI.String(): cmdWhoAmI, - "auth": Command{ - HelpText: "Authenticate to admin", - Function: func(cl *Client, args []string) string { - if cl.IsAdmin { - return "You are already authenticated." - } + common.CNAuth.String(): Command{ + HelpText: "Authenticate to admin", + Function: func(cl *Client, args []string) string { + if cl.IsAdmin { + return "You are already authenticated." + } - pw := html.UnescapeString(strings.Join(args, " ")) + pw := html.UnescapeString(strings.Join(args, " ")) - if settings.AdminPassword == pw { - cl.IsMod = true - cl.IsAdmin = true - fmt.Printf("[auth] %s used the admin password\n", cl.name) - return "Admin rights granted." - } + if settings.AdminPassword == pw { + cl.IsMod = true + cl.IsAdmin = true + fmt.Printf("[auth] %s used the admin password\n", cl.name) + return "Admin rights granted." + } - if cl.belongsTo.redeemModPass(pw) { - cl.IsMod = true - fmt.Printf("[auth] %s used a mod password\n", cl.name) - return "Moderator privileges granted." - } + if cl.belongsTo.redeemModPass(pw) { + cl.IsMod = true + fmt.Printf("[auth] %s used a mod password\n", cl.name) + return "Moderator privileges granted." + } - fmt.Printf("[auth] %s gave an invalid password\n", cl.name) - return "Invalid password." - }, + fmt.Printf("[auth] %s gave an invalid password\n", cl.name) + return "Invalid password." }, + }, - "users": Command{ - HelpText: "Show a list of users in chat", - Function: func(cl *Client, args []string) string { - names := cl.belongsTo.GetNames() - return strings.Join(names, " ") - }, + common.CNUsers.String(): Command{ + HelpText: "Show a list of users in chat", + Function: func(cl *Client, args []string) string { + names := cl.belongsTo.GetNames() + return strings.Join(names, " ") }, }, + }, - mod: map[string]Command{ - "sv": Command{ - HelpText: "Send a server announcement message. It will show up red with a border in chat.", - Function: func(cl *Client, args []string) string { - if len(args) == 0 { - return "Missing message" - } - svmsg := formatLinks(strings.Join(ParseEmotesArray(args), " ")) - //cl.belongsTo.AddCmdMsg(fmt.Sprintf(`
%s
`, svmsg)) - cl.belongsTo.AddMsg(cl, false, true, svmsg) - return "" - }, + mod: map[string]Command{ + common.CNSv.String(): Command{ + HelpText: "Send a server announcement message. It will show up red with a border in chat.", + Function: func(cl *Client, args []string) string { + if len(args) == 0 { + return "Missing message" + } + svmsg := formatLinks(strings.Join(ParseEmotesArray(args), " ")) + //cl.belongsTo.AddCmdMsg(fmt.Sprintf(`
%s
`, svmsg)) + cl.belongsTo.AddMsg(cl, false, true, svmsg) + return "" }, + }, - "playing": Command{ - HelpText: "Set the title text and info link.", - Function: func(cl *Client, args []string) string { - // Clear/hide title if sent with no arguments. - if len(args) == 0 { - cl.belongsTo.ClearPlaying() - return "" - } - link := "" - title := "" - - // pickout the link (can be anywhere, as long as there are no spaces). - for _, word := range args { - word = html.UnescapeString(word) - if strings.HasPrefix(word, "http://") || strings.HasPrefix(word, "https://") { - link = word - } else { - title = title + " " + word - } + common.CNPlaying.String(): Command{ + HelpText: "Set the title text and info link.", + Function: func(cl *Client, args []string) string { + // Clear/hide title if sent with no arguments. + if len(args) == 0 { + cl.belongsTo.ClearPlaying() + return "" + } + link := "" + title := "" + + // pickout the link (can be anywhere, as long as there are no spaces). + for _, word := range args { + word = html.UnescapeString(word) + if strings.HasPrefix(word, "http://") || strings.HasPrefix(word, "https://") { + link = word + } else { + title = title + " " + word } + } - cl.belongsTo.SetPlaying(title, link) - return "" - }, + cl.belongsTo.SetPlaying(title, link) + return "" }, + }, - "unmod": Command{ - HelpText: "Revoke a user's moderator privilages. Moderators can only unmod themselves.", - Function: func(cl *Client, args []string) string { - if len(args) > 0 && !cl.IsAdmin { - return "You can only unmod yourself, not others." - } + common.CNUnmod.String(): Command{ + HelpText: "Revoke a user's moderator privilages. Moderators can only unmod themselves.", + Function: func(cl *Client, args []string) string { + if len(args) > 0 && !cl.IsAdmin { + return "You can only unmod yourself, not others." + } - if len(args) == 0 { - cl.Unmod() - return "You have unmodded yourself." - } + if len(args) == 0 { + cl.Unmod() + return "You have unmodded yourself." + } - if err := cl.belongsTo.Unmod(args[0]); err != nil { - return err.Error() - } + if err := cl.belongsTo.Unmod(args[0]); err != nil { + return err.Error() + } - return fmt.Sprintf(`%s has been unmodded.`, args[0]) - }, + return fmt.Sprintf(`%s has been unmodded.`, args[0]) }, + }, - "kick": Command{ - HelpText: "Kick a user from chat.", - Function: func(cl *Client, args []string) string { - if len(args) == 0 { - return "Missing name to kick." - } - return cl.belongsTo.Kick(args[0]) - }, + common.CNKick.String(): Command{ + HelpText: "Kick a user from chat.", + Function: func(cl *Client, args []string) string { + if len(args) == 0 { + return "Missing name to kick." + } + return cl.belongsTo.Kick(args[0]) }, + }, - "ban": Command{ - HelpText: "Ban a user from chat. They will not be able to re-join chat, but will still be able to view the stream.", - Function: func(cl *Client, args []string) string { - if len(args) == 0 { - return "missing name to ban." - } - fmt.Printf("[ban] Attempting to ban %s\n", strings.Join(args, "")) - return cl.belongsTo.Ban(args[0]) - }, + common.CNBan.String(): Command{ + HelpText: "Ban a user from chat. They will not be able to re-join chat, but will still be able to view the stream.", + Function: func(cl *Client, args []string) string { + if len(args) == 0 { + return "missing name to ban." + } + fmt.Printf("[ban] Attempting to ban %s\n", strings.Join(args, "")) + return cl.belongsTo.Ban(args[0]) }, + }, - "unban": Command{ - HelpText: "Remove a ban on a user.", - Function: func(cl *Client, args []string) string { - if len(args) == 0 { - return "missing name to unban." - } - fmt.Printf("[ban] Attempting to unban %s\n", strings.Join(args, "")) + common.CNUnban.String(): Command{ + HelpText: "Remove a ban on a user.", + Function: func(cl *Client, args []string) string { + if len(args) == 0 { + return "missing name to unban." + } + fmt.Printf("[ban] Attempting to unban %s\n", strings.Join(args, "")) - err := settings.RemoveBan(args[0]) - if err != nil { - return err.Error() - } - return "" - }, + err := settings.RemoveBan(args[0]) + if err != nil { + return err.Error() + } + return "" }, }, + }, - admin: map[string]Command{ - "mod": Command{ - HelpText: "Grant moderator privilages to a user.", - Function: func(cl *Client, args []string) string { - if len(args) == 0 { - return "Missing user to mod." - } - if err := cl.belongsTo.Mod(args[0]); err != nil { - return err.Error() - } - return fmt.Sprintf(`%s has been modded.`, args[0]) - }, + admin: map[string]Command{ + common.CNMod.String(): Command{ + HelpText: "Grant moderator privilages to a user.", + Function: func(cl *Client, args []string) string { + if len(args) == 0 { + return "Missing user to mod." + } + if err := cl.belongsTo.Mod(args[0]); err != nil { + return err.Error() + } + return fmt.Sprintf(`%s has been modded.`, args[0]) }, + }, - "reloadplayer": Command{ - HelpText: "Reload the stream player for everybody in chat.", - Function: func(cl *Client, args []string) string { - //cl.belongsTo.AddCmdMsg(`[SERVER] Video player reload forced.
`) - cl.belongsTo.AddCmdMsg(common.CMD_REFRESHPLAYER, nil) - return "Reloading player for all chatters." - }, + common.CNReloadPlayer.String(): Command{ + HelpText: "Reload the stream player for everybody in chat.", + Function: func(cl *Client, args []string) string { + //cl.belongsTo.AddCmdMsg(`[SERVER] Video player reload forced.
`) + cl.belongsTo.AddCmdMsg(common.CmdRefreshPlayer, nil) + return "Reloading player for all chatters." }, + }, - "reloademotes": Command{ - HelpText: "Reload the emotes on the server.", - Function: func(cl *Client, args []string) string { - cl.ServerMessage("Reloading emotes") - num, err := LoadEmotes() - if err != nil { - fmt.Printf("Unbale to reload emotes: %s\n", err) - return fmt.Sprintf("ERROR: %s", err) - } + common.CNReloadEmotes.String(): Command{ + HelpText: "Reload the emotes on the server.", + Function: func(cl *Client, args []string) string { + cl.ServerMessage("Reloading emotes") + num, err := LoadEmotes() + if err != nil { + fmt.Printf("Unbale to reload emotes: %s\n", err) + return fmt.Sprintf("ERROR: %s", err) + } - fmt.Printf("Loaded %d emotes\n", num) - return fmt.Sprintf("Emotes loaded: %d", num) - }, + fmt.Printf("Loaded %d emotes\n", num) + return fmt.Sprintf("Emotes loaded: %d", num) }, + }, - "modpass": Command{ - HelpText: "Generate a single-use mod password.", - Function: func(cl *Client, args []string) string { - password := cl.belongsTo.generateModPass() - return "Single use password: " + password - }, + common.CNModpass.String(): Command{ + HelpText: "Generate a single-use mod password.", + Function: func(cl *Client, args []string) string { + password := cl.belongsTo.generateModPass() + return "Single use password: " + password }, - - //"reloadsettings": func(cl *Client, args []string) string { - // return "" - //}, }, - } + + //"reloadsettings": func(cl *Client, args []string) string { + // return "" + //}, + }, } func (cc *CommandControl) RunCommand(command string, args []string, sender *Client) string { @@ -289,8 +284,6 @@ func cmdHelp(cl *Client, args []string) string { return `Opening help in new window.` } -var hlpTemplate = `
%s
%s
` - // Return a full HTML page for the help text. This should probably be rewritten with templates. func helpPage(ismod, isadmin bool) string { if commands == nil { @@ -298,20 +291,22 @@ func helpPage(ismod, isadmin bool) string { } text := []string{} - for key, cmd := range commands.user { - text = append(text, fmt.Sprintf(hlpTemplate, key, cmd.HelpText)) + appendText := func(group map[string]Command) { + for key, cmd := range group { + for _, k := range strings.Split(key, common.CommandNameSeparator) { + text = append(text, fmt.Sprintf(`
%s
%s
`, k, cmd.HelpText)) + } + } } + appendText(commands.user) + if ismod { - for key, cmd := range commands.mod { - text = append(text, fmt.Sprintf(hlpTemplate, key, cmd.HelpText)) - } + appendText(commands.mod) } if isadmin { - for key, cmd := range commands.mod { - text = append(text, fmt.Sprintf(hlpTemplate, key, cmd.HelpText)) - } + appendText(commands.admin) } // This is ugly diff --git a/chatroom.go b/chatroom.go index 75023da..c9f502e 100644 --- a/chatroom.go +++ b/chatroom.go @@ -152,14 +152,14 @@ func (cr *ChatRoom) Join(name, uid string) (*Client, error) { fmt.Printf("[join] %s %s\n", host, name) //client.Send(cr.GetPlayingString()) - playingCommand, err := common.EncodeCommand(common.CMD_PLAYING, []string{cr.playing, cr.playingLink}) + playingCommand, err := common.EncodeCommand(common.CmdPlaying, []string{cr.playing, cr.playingLink}) if err != nil { fmt.Printf("Unable to encode playing command on join: %s\n", err) } else { client.Send(playingCommand) } //cr.AddMsg(fmt.Sprintf("%s has joined the chat.
", client.color, name)) - cr.AddEventMsg(common.EV_JOIN, name, client.color) + cr.AddEventMsg(common.EvJoin, name, client.color) return client, nil } @@ -178,7 +178,7 @@ func (cr *ChatRoom) Leave(name, color string) { cr.delClient(name) //cr.AddMsg(fmt.Sprintf("%s has left the chat.
", color, name)) - cr.AddEventMsg(common.EV_LEAVE, name, color) + cr.AddEventMsg(common.EvLeave, name, color) fmt.Printf("[leave] %s %s\n", client.Host(), client.name) } @@ -206,7 +206,7 @@ func (cr *ChatRoom) Kick(name string) string { cr.delClient(name) //cr.AddMsg(fmt.Sprintf("%s has been kicked.
", name)) - cr.AddEventMsg(common.EV_KICK, name, color) + cr.AddEventMsg(common.EvKick, name, color) fmt.Printf("[kick] %s %s has been kicked\n", host, name) return "" } @@ -243,24 +243,24 @@ func (cr *ChatRoom) Ban(name string) string { if err != nil { fmt.Printf("[BAN] Error banning %q: %s\n", name, err) //cr.AddMsg(fmt.Sprintf("%s has been kicked.
", name)) - cr.AddEventMsg(common.EV_KICK, name, color) + cr.AddEventMsg(common.EvKick, name, color) } else { //cr.AddMsg(fmt.Sprintf("%s has been banned.
", name)) - cr.AddEventMsg(common.EV_BAN, name, color) + cr.AddEventMsg(common.EvBan, name, color) } return "" } // Add a chat message from a viewer func (cr *ChatRoom) AddMsg(from *Client, isAction, isServer bool, msg string) { - t := common.MSG_CHAT + t := common.MsgChat if isAction { - t = common.MSG_ACTION + t = common.MsgAction } if isServer { - t = common.MSG_SERVER + t = common.MsgServer } data, err := common.EncodeMessage( @@ -378,14 +378,14 @@ func (cr *ChatRoom) ClearPlaying() { cr.playing = "" cr.playingLink = "" //cr.AddCmdMsg(``) - cr.AddCmdMsg(common.CMD_PLAYING, []string{"", ""}) + cr.AddCmdMsg(common.CmdPlaying, []string{"", ""}) } func (cr *ChatRoom) SetPlaying(title, link string) { cr.playing = title cr.playingLink = link //cr.AddCmdMsg(cr.GetPlayingString()) - cr.AddCmdMsg(common.CMD_PLAYING, []string{title, link}) + cr.AddCmdMsg(common.CmdPlaying, []string{title, link}) } //func (cr *ChatRoom) GetPlayingString() string { diff --git a/common/chatdata.go b/common/chatdata.go index 143e73d..a795e5f 100644 --- a/common/chatdata.go +++ b/common/chatdata.go @@ -7,6 +7,38 @@ import ( "strings" ) +const CommandNameSeparator = "," + +type ChatCommandNames []string + +func (c ChatCommandNames) String() string { + return strings.Join(c, CommandNameSeparator) +} + +// Names for commands +var ( + // User Commands + CNMe ChatCommandNames = []string{"me"} + CNHelp ChatCommandNames = []string{"help"} + CNCount ChatCommandNames = []string{"count"} + CNColor ChatCommandNames = []string{"color", "colour"} + CNWhoAmI ChatCommandNames = []string{"w", "whoami"} + CNAuth ChatCommandNames = []string{"auth"} + CNUsers ChatCommandNames = []string{"users"} + // Mod Commands + CNSv ChatCommandNames = []string{"sv"} + CNPlaying ChatCommandNames = []string{"playing"} + CNUnmod ChatCommandNames = []string{"unmod"} + CNKick ChatCommandNames = []string{"kick"} + CNBan ChatCommandNames = []string{"ban"} + CNUnban ChatCommandNames = []string{"unban"} + // Admin Commands + CNMod ChatCommandNames = []string{"mod"} + CNReloadPlayer ChatCommandNames = []string{"reloadplayer"} + CNReloadEmotes ChatCommandNames = []string{"reloademotes"} + CNModpass ChatCommandNames = []string{"modpass"} +) + type ChatData struct { Type DataType Data DataInterface @@ -40,29 +72,30 @@ type DataInterface interface { } func (dc DataMessage) GetType() DataType { - return DT_CHAT + return DTChat } func (de DataError) GetType() DataType { - return DT_ERROR + return DTError } func (dc DataCommand) GetType() DataType { - return DT_COMMAND + return DTCommand } func (de DataEvent) GetType() DataType { - return DT_EVENT + return DTEvent } type DataType int +// Data Types const ( - DT_INVALID DataType = iota - DT_CHAT // chat message - DT_ERROR // something went wrong with the previous request - DT_COMMAND // non-chat function - DT_EVENT // join/leave/kick/ban events + DTInvalid DataType = iota + DTChat // chat message + DTError // something went wrong with the previous request + DTCommand // non-chat function + DTEvent // join/leave/kick/ban events ) func ParseDataType(token json.Token) (DataType, error) { @@ -70,51 +103,54 @@ func ParseDataType(token json.Token) (DataType, error) { val, err := strconv.ParseInt(d, 10, 32) if err != nil { fmt.Printf("Invalid data type value: %q\n", d) - return DT_INVALID, err + return DTInvalid, err } return DataType(val), nil } type CommandType int +// Command Types const ( - CMD_PLAYING CommandType = iota - CMD_REFRESHPLAYER - CMD_PURGECHAT - CMD_HELP + CmdPlaying CommandType = iota + CmdRefreshPlayer + CmdPurgeChat + CmdHelp ) type EventType int +// Event Types const ( - EV_JOIN EventType = iota - EV_LEAVE - EV_KICK - EV_BAN - EV_SERVERMESSAGE + EvJoin EventType = iota + EvLeave + EvKick + EvBan + EvServerMessage ) type MessageType int +// Message Types const ( - MSG_CHAT MessageType = iota // standard chat - MSG_ACTION // /me command - MSG_SERVER // server message - MSG_ERROR + MsgChat MessageType = iota // standard chat + MsgAction // /me command + MsgServer // server message + MsgError ) // TODO: Read this HTML from a template somewhere func (dc DataMessage) HTML() string { fmt.Printf("message type: %d\n", dc.Type) switch dc.Type { - case MSG_ACTION: + case MsgAction: return `
` + dc.From + ` ` + dc.Message + `
` - case MSG_SERVER: + case MsgServer: return `
` + dc.Message + `
` - case MSG_ERROR: + case MsgError: return `
` + dc.Message + `
` default: @@ -125,16 +161,16 @@ func (dc DataMessage) HTML() string { func (de DataEvent) HTML() string { switch de.Event { - case EV_KICK: + case EvKick: return `
` + de.User + ` has been kicked.
` - case EV_LEAVE: + case EvLeave: return `
` + de.User + ` has left the chat.
` - case EV_BAN: + case EvBan: return `
` + de.User + ` has been banned.
` - case EV_JOIN: + case EvJoin: return `
` + de.User + ` has joined the chat.
` } @@ -151,7 +187,7 @@ func (de DataCommand) HTML() string { func EncodeMessage(name, color, msg string, msgtype MessageType) (string, error) { d := ChatData{ - Type: DT_CHAT, + Type: DTChat, Data: DataMessage{ From: name, Color: color, @@ -165,7 +201,7 @@ func EncodeMessage(name, color, msg string, msgtype MessageType) (string, error) func EncodeError(message string) (string, error) { d := ChatData{ - Type: DT_ERROR, + Type: DTError, Data: DataError{Message: message}, } return jsonifyChatData(d) @@ -173,7 +209,7 @@ func EncodeError(message string) (string, error) { func EncodeCommand(command CommandType, args []string) (string, error) { d := ChatData{ - Type: DT_COMMAND, + Type: DTCommand, Data: DataCommand{ Command: command, Arguments: args, @@ -184,7 +220,7 @@ func EncodeCommand(command CommandType, args []string) (string, error) { func EncodeEvent(event EventType, name, color string) (string, error) { d := ChatData{ - Type: DT_EVENT, + Type: DTEvent, Data: DataEvent{ Event: event, User: name, @@ -231,25 +267,25 @@ func DecodeData(rawjson string) (DataInterface, error) { } else { switch DataType(data.Type) { - case DT_CHAT: + case DTChat: d := DataMessage{} if err := decoder.Decode(&d); err != nil { return nil, fmt.Errorf("Unable to decode DataMessage: %s", err) } return d, nil - case DT_ERROR: + case DTError: d := DataError{} if err := decoder.Decode(&d); err != nil { return nil, fmt.Errorf("Unable to decode DataError: %s", err) } return d, nil - case DT_COMMAND: + case DTCommand: d := DataCommand{} if err := decoder.Decode(&d); err != nil { return nil, fmt.Errorf("Unable to decode DataCommand: %s", err) } return d, nil - case DT_EVENT: + case DTEvent: d := DataEvent{} if err := decoder.Decode(&d); err != nil { return nil, fmt.Errorf("Unable to decode DataEvent: %s", err) diff --git a/wasm/main_wasm.go b/wasm/main_wasm.go index e5f2c4e..29b84c8 100644 --- a/wasm/main_wasm.go +++ b/wasm/main_wasm.go @@ -15,7 +15,7 @@ func recieve(v []js.Value) { } fmt.Printf("Received: %s\n", v[0]) - data, err := common.DecodeData(fmt.Sprintf("%s", v[0])) + data, err := common.DecodeData(v[0].String()) if err != nil { fmt.Printf("Error decoding data: %s\n", err) js.Call("appendMessages", v) @@ -23,13 +23,13 @@ func recieve(v []js.Value) { } switch data.GetType() { - case common.DT_CHAT, common.DT_EVENT, common.DT_ERROR: + case common.DTChat, common.DTError, common.DTEvent: js.Call("appendMessages", data.HTML()) - case common.DT_COMMAND: + case common.DTCommand: dc := data.(common.DataCommand) switch dc.Command { - case common.CMD_PLAYING: + case common.CmdPlaying: if dc.Arguments == nil || len(dc.Arguments) == 0 { js.Call("setPlaying", "", "") @@ -39,11 +39,11 @@ func recieve(v []js.Value) { } else if len(dc.Arguments) == 2 { js.Call("setPlaying", dc.Arguments[0], dc.Arguments[1]) } - case common.CMD_REFRESHPLAYER: + case common.CmdRefreshPlayer: js.Call("initPlayer", nil) - case common.CMD_PURGECHAT: + case common.CmdPurgeChat: fmt.Println("//TODO: chat purge command received.") - case common.CMD_HELP: + case common.CmdHelp: js.Call("appendMesages", data.HTML()) // TODO: open window //js.Call("")