Skip to content

Commit

Permalink
Rewrote string coloring to use a custom markup syntax:
Browse files Browse the repository at this point in the history
* e.g. "{red|some red text}"
* the client is responsible for displaying it correctly
* removes the use of html, allowing the server to escape it properly
* fixes #18
  • Loading branch information
doughsay committed Feb 15, 2013
1 parent 0e77dca commit 92b9058
Show file tree
Hide file tree
Showing 7 changed files with 68 additions and 78 deletions.
51 changes: 36 additions & 15 deletions assets/js/view_models/client.coffee
Original file line number Diff line number Diff line change
@@ -1,9 +1,28 @@
# Knockout.js view model for the room.js client
class ClientView

# apply styles to a string using a span
c = (str, styles) ->
"<span class='#{styles}'>#{str}</span>"
# apply styles to a color marked up string using a span
colorize = (str) ->
str
.replace(/\\\{/g, "!~TEMP_SWAP_LEFT~!")
.replace(/\\\}/g, "!~TEMP_SWAP_RIGHT~!")
.replace(/\{(.*?)\|/g, "<span class='$1'>")
.replace(/\}/g, "</span>")
.replace(/!~TEMP_SWAP_LEFT~!/g, "{")
.replace(/!~TEMP_SWAP_RIGHT~!/g, "}")

# escape any html in a string
escapeHTML = (str) ->
str
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')

# escape curly brackets in a string
escapeBrackets = (str) ->
str
.replace(/\{/g, '\\{')
.replace(/\}/g, '\\}')

lines: ko.observableArray []
maxLines: ko.observable 1000
Expand Down Expand Up @@ -95,8 +114,9 @@ class ClientView
@screen.scrollTop(@screen[0].scrollHeight);

# add a line of output from the server to the screen
addLine: (line) ->
@lines.push line
addLine: (line, escape = true) ->
line = escapeHTML line if escape
@lines.push colorize line
if @lines().length > @maxLines()
@lines.shift()
@scrollToBottom()
Expand All @@ -109,8 +129,9 @@ class ClientView
# and add it to the command history
sendCommand: ->
command = @command()
escapedCommand = escapeBrackets command
if command
@addLine c '\n> '+command, 'gray'
@addLine "\n{gray|> #{escapedCommand}}", false
@history.unshift command
if @history.length > @maxHistory()
@history.pop()
Expand All @@ -121,7 +142,7 @@ class ClientView
@inputCallback command
@inputCallback = null
else
@socket.emit 'input', command
@socket.emit 'input', escapedCommand
@command ""

# simple client-side commands
Expand Down Expand Up @@ -184,30 +205,30 @@ class ClientView
#############################

connect: =>
@addLine c 'Connected!', 'bold green'
@addLine '{bold green|Connected!}'

connecting: =>
@addLine c "Connecting...", 'gray'
@addLine '{gray|Connecting...}'

disconnect: =>
@addLine c 'Disconnected from server.', 'bold red'
@addLine '{bold red|Disconnected from server.}'
@loadedVerb null
@form null

connect_failed: =>
@addLine c 'Connection to server failed.', 'bold red'
@addLine '{bold red|Connection to server failed.}'

error: =>
@addLine c 'An unknown error occurred.', 'bold red'
@addLine '{bold red|An unknown error occurred.}'

reconnect_failed: =>
@addLine c 'Unable to reconnect to server.', 'bold red'
@addLine '{bold red|Unable to reconnect to server.}'

reconnect: =>
# @addLine c 'Reconnected!', 'bold green'
# @addLine '{bold green|Reconnected!}'

reconnecting: =>
@addLine c "Attempting to reconnect...", 'gray'
@addLine '{gray|Attempting to reconnect...}'

# output event
# adds a line of output to the screen
Expand Down
21 changes: 0 additions & 21 deletions lib/color.coffee

This file was deleted.

6 changes: 2 additions & 4 deletions lib/context.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ coffee = require 'coffee-script'
_ = require 'underscore'
util = require 'util'

color = require('./color').color
connections = require './connection_manager'
mooUtil = require './util'
mooBrowser = require './moo_browser'
Expand All @@ -14,7 +13,6 @@ class Context
constructor: (@db, @player, @memo = {}) ->
base =
eval: undefined
c: color
$: (id) => @contextify @db.findById(id)
$player: if @player? then @contextify @player else @contextify @db.nothing
$here: if @player?.location()? then @contextify @player.location() else @contextify @db.nothing
Expand Down Expand Up @@ -92,9 +90,9 @@ class Context
if @player? and @player isnt @db.nothing
runner = @player.toString()
if stack
@player.send error.stack.split('\n').map((line) -> color line, 'inverse bold red').join('\n')
@player.send error.stack.split('\n').map((line) -> "{inverse bold red|#{line}}").join('\n')
else
@player.send color "#{errorStr} in '#{source}'", 'inverse bold red'
@player.send "{inverse bold red|#{errorStr} in '#{source}'}"

util.log "#{runner} caused exception: #{errorStr} in '#{source}'"

Expand Down
4 changes: 1 addition & 3 deletions lib/db.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ fs = require 'fs'
util = require 'util'
_ = require 'underscore'

c = require('./color').color

connections = require './connection_manager'
mooUtil = require './util'
context = require './context'
Expand Down Expand Up @@ -48,7 +46,7 @@ exports.RoomJsDb = class
for player in @players
socket = connections.socketFor player
if socket?
socket.emit 'output', c 'Server shutting down.', 'red'
socket.emit 'output', '{red|Server shutting down.}'
socket.disconnect()
@saveSync()

Expand Down
4 changes: 1 addition & 3 deletions lib/moo_browser.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
http = require 'http'
util = require 'util'

c = require('./color').color

get = (host, path = '/', callback) ->
throw new Error "No host specified" if not host?

Expand All @@ -23,7 +21,7 @@ get = (host, path = '/', callback) ->

client.on 'error', (error) ->
try
callback? c error.toString(), 'inverse bold red'
callback? "{inverse bold red|#{error.toString()}}"
catch error
util.log "Caught Exception in browser callback: #{error.toString()}"

Expand Down
35 changes: 16 additions & 19 deletions lib/util.coffee
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
# Some usful utlity functions.

c = require('./color').color

# replace newlines in a string with '\n'
safeString = (s) ->
s.replace /\n/g, '\\n'
Expand Down Expand Up @@ -39,37 +37,37 @@ print = (x, maxdepth, depth = 0, prefix = '', parents = []) ->

output = do -> switch typeof x
when 'number'
c x, 'yellow'
"{yellow|#{x}}"
when 'string'
if depth == 0
"'" + (c (safeString x), 'green') + "'"
"'{green|#{safeString x}}'"
else
"'" + (c (truncate safeString x), 'green') + "'"
"'{green|#{truncate safeString x}}'"
when 'boolean'
c x.toString(), 'magenta'
"{magenta|#{x.toString()}}"
when 'undefined'
c 'undefined', 'gray'
'{black|undefined}'
when 'function'
if x.verb
if x.hidden
(c '[', 'cyan') + (c 'Private', 'cyan bold') + (c ' Verb]', 'cyan')
'{cyan|[{bold|Private} Verb]}'
else
c '[Verb]', 'cyan'
'{cyan|[Verb]}'
else
c '[Function]', 'gray'
'{gray|[Function]}'
when 'object'
if x == null
c 'null', 'red'
'{red|null}'
else
if x in parents
c '[CircularReference]', 'yellow inverse'
'{yellow inverse|[CircularReference]}'
else
parents.push x
if Array.isArray x
if x.length == 0
output = '[]'
else if maxdepth == depth
output = c "[Array]", 'blue'
output = '{blue|[Array]}'
else
xs = (x.map (y) -> print y, maxdepth, depth+1, '', parents)
xs[0] = '[ ' + (xs[0].replace indent + ' ', '')
Expand All @@ -79,16 +77,15 @@ print = (x, maxdepth, depth = 0, prefix = '', parents = []) ->
output = xs.join ',\n'
else
if (key for key of x).length == 0
output = '{}'
output = '\\{\\}'
else if maxdepth == depth
output = c x.toString(), 'blue'
output = "{blue|#{x.toString()}}"
else
xs = []
for key, value of x
keyColor = 'blue'
xs.push print value, maxdepth, depth+1, (c key, keyColor) + ': ', parents
xs[0] = '{ ' + (xs[0].replace indent + ' ', '')
xs[xs.length-1] += ' }'
xs.push print value, maxdepth, depth+1, "{blue|#{key}}" + ': ', parents
xs[0] = '\\{ ' + (xs[0].replace indent + ' ', '')
xs[xs.length-1] += ' \\}'
if prefix != ''
xs[0] = '\n' + indent + xs[0]
output = xs.join ',\n'
Expand Down
25 changes: 12 additions & 13 deletions socket_server.coffee
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
io = require 'socket.io'

phash = require('./lib/hash').phash
c = require('./lib/color').color
parse = require('./lib/parser').parse

connections = require './lib/connection_manager'
Expand All @@ -15,8 +14,8 @@ class RoomJsSocket

# welcome the socket and attach the event listeners
constructor: (@db, @socket) ->
@socket.emit 'output', "Welcome to #{c 'room.js', 'blue bold'}!"
@socket.emit 'output', "Type #{c 'help', 'magenta bold'} for a list of available commands."
@socket.emit 'output', "Welcome to {blue bold|room.js}!"
@socket.emit 'output', "Type {magenta bold|help} for a list of available commands."

@socket.on 'disconnect', @onDisconnect
@socket.on 'input', @onInput
Expand Down Expand Up @@ -52,17 +51,17 @@ class RoomJsSocket
when "help"
msg = """
\nAvailable commands:
#{c 'login', 'magenta bold'} - login to an existing account
#{c 'create', 'magenta bold'} - create a new account
#{c 'help', 'magenta bold'} - show this message
{magenta bold|login} - login to an existing account
{magenta bold|create} - create a new account
{magenta bold|help} - show this message
"""
@socket.emit 'output', msg
when "login"
@socket.emit 'request_form_input', formDescriptors.login()
when "create"
@socket.emit 'request_form_input', formDescriptors.createAccount()
else
@socket.emit 'output', "\nUnrecognized command. Type #{c 'help', 'magenta bold'} for a list of available commands."
@socket.emit 'output', "\nUnrecognized command. Type {magenta bold|help} for a list of available commands."

# handle a player command
onPlayerCommand: (player, str) =>
Expand Down Expand Up @@ -96,7 +95,7 @@ class RoomJsSocket
self = player.location()
context.runVerb @db, player, huhVerb, self, dobj, iobj, verbstr, argstr, dobjstr, prepstr, iobjstr
else
player.send c("I didn't understand that.", 'gray')
player.send "{gray|I didn't understand that.}"

# handle log in form submission
onLogin: (userData, fn) =>
Expand All @@ -114,7 +113,7 @@ class RoomJsSocket

other_socket = connections.socketFor player
if other_socket?
player.send c "Disconnected by another login.", 'red bold'
player.send "{red bold|Disconnected by another login.}"
other_socket.disconnect()

connections.add player, @socket
Expand Down Expand Up @@ -261,19 +260,19 @@ class RoomJsSocket

if errors.length > 0
errors.unshift 'There were errors in your verb code submission:'
player.send c (errors.join '\n'), 'red'
player.send '{red|'+(errors.join '\n')+'}'
fn {error: true, verb: verb}
else
id = verb.oid
object = @db.findById(id)
object.saveVerb verb
player.send c "Verb saved!", 'green'
player.send '{green|Verb saved!}'
fn {error: false, verb: verb}
else
player.send c "You are not allowed to do that.", 'red'
player.send '{red|You are not allowed to do that.}'
fn {error: true}
else
@socket.emit 'output', c "You are not logged in.", 'red'
@socket.emit 'output', "{red|You are not logged in.}"
fn {error: true}

# This is the websocket server.
Expand Down

0 comments on commit 92b9058

Please sign in to comment.