Implemented ansi-colour backgrounds in webclient. Added a new @color command for displaying colour spaces. Also changed a number of other features outlined in Issue 309.

This commit is contained in:
Griatch 2012-10-24 21:41:07 +02:00
parent e534d5f9a0
commit ec46465656
7 changed files with 177 additions and 61 deletions

View file

@ -29,6 +29,7 @@ class DefaultCmdSet(CmdSet):
self.add(general.CmdDrop()) self.add(general.CmdDrop())
self.add(general.CmdSay()) self.add(general.CmdSay())
self.add(general.CmdAccess()) self.add(general.CmdAccess())
self.add(general.CmdColorTest())
# The help system # The help system
self.add(help.CmdHelp()) self.add(help.CmdHelp())

View file

@ -13,7 +13,7 @@ from src.commands.default.muxcommand import MuxCommand, MuxCommandOOC
__all__ = ("CmdHome", "CmdLook", "CmdPassword", "CmdNick", __all__ = ("CmdHome", "CmdLook", "CmdPassword", "CmdNick",
"CmdInventory", "CmdGet", "CmdDrop", "CmdQuit", "CmdWho", "CmdInventory", "CmdGet", "CmdDrop", "CmdQuit", "CmdWho",
"CmdSay", "CmdPose", "CmdEncoding", "CmdAccess", "CmdSay", "CmdPose", "CmdEncoding", "CmdAccess",
"CmdOOCLook", "CmdIC", "CmdOOC") "CmdOOCLook", "CmdIC", "CmdOOC", "CmdColorTest")
AT_SEARCH_RESULT = utils.variable_from_module(*settings.SEARCH_AT_RESULT.rsplit('.', 1)) AT_SEARCH_RESULT = utils.variable_from_module(*settings.SEARCH_AT_RESULT.rsplit('.', 1))
BASE_PLAYER_TYPECLASS = settings.BASE_PLAYER_TYPECLASS BASE_PLAYER_TYPECLASS = settings.BASE_PLAYER_TYPECLASS
@ -753,3 +753,61 @@ class CmdOOC(MuxCommandOOC):
caller.msg("\n{GYou go OOC.{n\n") caller.msg("\n{GYou go OOC.{n\n")
caller.execute_cmd("look") caller.execute_cmd("look")
class CmdColorTest(MuxCommand):
"""
testing colors
Usage:
@color ansi|xterm256
Print a color map along with in-mud color codes, while testing what is supported in your client.
Choices are 16-color ansi (supported in most muds) or the 256-color xterm256 standard.
No checking is done to determine your client supports color - if not you will
see rubbish appear.
"""
key = "@color"
locks = "cmd:all()"
help_category = "General"
def func(self):
"Show color tables"
if not self.args or not self.args in ("ansi", "xterm256"):
self.caller.msg("Usage: @color ansi|xterm256")
return
if self.args == "ansi":
from src.utils import ansi
ap = ansi.ANSI_PARSER
# ansi colors
# show all ansi color-related codes
col1 = ["%s%s{n" % (code, code.replace("{","{{")) for code, _ in ap.ext_ansi_map[:-1]]
hi = "%ch"
col2 = ["%s%s{n" % (code, code.replace("%", "%%")) for code, _ in ap.mux_ansi_map[:-2]]
col3 = ["%s%s{n" % (hi+code, (hi+code).replace("%", "%%")) for code, _ in ap.mux_ansi_map[:-2]]
table = utils.format_table([col1, col2, col3], extra_space=1)
string = "ANSI colors:"
for row in table:
string += "\n" + "".join(row)
print string
self.caller.msg(string)
self.caller.msg("{{X and %%cx are black-on-black)")
elif self.args == "xterm256":
table = [[],[],[],[],[],[],[],[],[],[],[],[]]
for ir in range(6):
for ig in range(6):
for ib in range(6):
# foreground table
table[ir].append("%%c%i%i%i%s{n" % (ir,ig,ib, "{{%i%i%i" % (ir,ig,ib)))
# background table
table[6+ir].append("%%cb%i%i%i%%c%i%i%i%s{n" % (ir,ig,ib,
5-ir,5-ig,5-ib,
"{{b%i%i%i" % (ir,ig,ib)))
table = utils.format_table(table)
string = "Xterm256 colors:"
for row in table:
string += "\n" + "".join(row)
self.caller.msg(string)
self.caller.msg("(e.g. %%c123 and %%cb123 also work)")

View file

@ -690,6 +690,7 @@ class CmdServerLoad(MuxCommand):
string += "\n{w On-entity Attribute cache usage:{n %5.2f MB (%i items)" % (size, count) string += "\n{w On-entity Attribute cache usage:{n %5.2f MB (%i items)" % (size, count)
caller.msg(string) caller.msg(string)
# class CmdPs(MuxCommand): # class CmdPs(MuxCommand):
# """ # """
# list processes # list processes

View file

@ -243,7 +243,7 @@ class WebClientSession(session.Session):
if raw: if raw:
self.client.lineSend(self.suid, string) self.client.lineSend(self.suid, string)
else: else:
self.client.lineSend(self.suid, parse_html(ansi.parse_ansi(string, strip_ansi=nomarkup))) self.client.lineSend(self.suid, parse_html(string, strip_ansi=nomarkup))
return return
except Exception, e: except Exception, e:
logger.log_trace() logger.log_trace()

View file

@ -75,15 +75,12 @@ class ANSIParser(object):
# MUX-style mappings %cr %cn etc # MUX-style mappings %cr %cn etc
self.mux_ansi_map = [ self.mux_ansi_map = [
(r'%r', ANSI_RETURN), # commented out by default; they (especially blink) are potentially annoying
(r'%t', ANSI_TAB), #(r'%r', ANSI_RETURN),
(r'%b', ANSI_SPACE), #(r'%t', ANSI_TAB),
(r'%cf', ANSI_BLINK), #(r'%b', ANSI_SPACE),
(r'%ci', ANSI_INVERSE), #(r'%cf', ANSI_BLINK),
(r'%ch', ANSI_HILITE), #(r'%ci', ANSI_INVERSE),
(r'%cn', ANSI_NORMAL),
(r'%cx', ANSI_BLACK),
(r'%cX', ANSI_BACK_BLACK),
(r'%cr', ANSI_RED), (r'%cr', ANSI_RED),
(r'%cR', ANSI_BACK_RED), (r'%cR', ANSI_BACK_RED),
(r'%cg', ANSI_GREEN), (r'%cg', ANSI_GREEN),
@ -98,6 +95,10 @@ class ANSIParser(object):
(r'%cC', ANSI_BACK_CYAN), (r'%cC', ANSI_BACK_CYAN),
(r'%cw', ANSI_WHITE), (r'%cw', ANSI_WHITE),
(r'%cW', ANSI_BACK_WHITE), (r'%cW', ANSI_BACK_WHITE),
(r'%cx', ANSI_BLACK),
(r'%cX', ANSI_BACK_BLACK),
(r'%ch', ANSI_HILITE),
(r'%cn', ANSI_NORMAL),
] ]
# Expanded mapping {r {n etc # Expanded mapping {r {n etc

View file

@ -11,7 +11,7 @@ snippet #577349 on http://code.activestate.com.
import re import re
import cgi import cgi
from src.utils import ansi from ansi import *
class TextToHTMLparser(object): class TextToHTMLparser(object):
""" """
@ -20,47 +20,80 @@ class TextToHTMLparser(object):
tabstop = 4 tabstop = 4
# mapping html color name <-> ansi code. # mapping html color name <-> ansi code.
# note that \[ is used here since they go into regexes. hilite = ANSI_HILITE
colorcodes = [('white', '\033\[1m\033\[37m'), normal = ANSI_NORMAL
('cyan', '\033\[1m\033\[36m'), underline = ANSI_UNDERLINE
('blue', '\033\[1m\033\[34m'), colorcodes = [
('red', '\033\[1m\033\[31m'), ('red', hilite + ANSI_RED),
('magenta', '\033\[1m\033\[35m'), ('maroon', ANSI_RED),
('lime', '\033\[1m\033\[32m'), ('lime', hilite + ANSI_GREEN),
('yellow', '\033\[1m\033\[33m'), ('green', ANSI_GREEN),
('gray', '\033\[37m'), ('yellow', hilite + ANSI_YELLOW),
('teal', '\033\[36m'), ('olive', ANSI_YELLOW),
('navy', '\033\[34m'), ('blue', hilite + ANSI_BLUE),
('maroon', '\033\[31m'), ('navy', ANSI_BLUE),
('purple', '\033\[35m'), ('magenta', hilite + ANSI_MAGENTA),
('green', '\033\[32m'), ('purple', ANSI_MAGENTA),
('olive', '\033\[33m')] ('cyan', hilite + ANSI_CYAN),
normalcode = '\033\[0m' ('teal', ANSI_CYAN),
bold = '\033\[1m' ('white', hilite + ANSI_WHITE), # pure white
underline = '\033\[4m' ('gray', ANSI_WHITE), #light grey
codestop = "|".join(co[1] for co in colorcodes + [("", normalcode), ("", bold), ("", underline), ("", "$")]) ('dimgray', hilite + ANSI_BLACK), #dark grey
('black', ANSI_BLACK), #pure black
]
colorback = [
('bgred', hilite + ANSI_BACK_RED),
('bgmaroon', ANSI_BACK_RED),
('bglime', hilite + ANSI_BACK_GREEN),
('bggreen', ANSI_BACK_GREEN),
('bgyellow', hilite + ANSI_BACK_YELLOW),
('bgolive', ANSI_BACK_YELLOW),
('bgblue', hilite + ANSI_BACK_BLUE),
('bgnavy', ANSI_BACK_BLUE),
('bgmagenta', hilite + ANSI_BACK_MAGENTA),
('bgpurple', ANSI_BACK_MAGENTA),
('bgcyan', hilite + ANSI_BACK_CYAN),
('bgteal', ANSI_BACK_CYAN),
('bgwhite', hilite + ANSI_BACK_WHITE),
('bggray', ANSI_BACK_WHITE),
('bgdimgray', hilite + ANSI_BACK_BLACK),
('bgblack', ANSI_BACK_BLACK),
]
# make sure to escape [
colorcodes = [(c, code.replace("[",r"\[")) for c, code in colorcodes]
colorback = [(c, code.replace("[",r"\[")) for c, code in colorback]
# create stop markers
fgstop = [("", c.replace("[", r"\[")) for c in (normal, hilite, underline)]
bgstop = [("", c.replace("[", r"\[")) for c in (normal,)]
fgstop = "|".join(co[1] for co in colorcodes + fgstop + [("", "$")])
bgstop = "|".join(co[1] for co in colorback + bgstop + [("", "$")])
# pre-compile regexes
re_fgs = [(cname, re.compile("(?:%s)(.*?)(?=%s)" % (code, fgstop))) for cname, code in colorcodes]
re_bgs = [(cname, re.compile("(?:%s)(.*?)(?=%s)" % (code, bgstop))) for cname, code in colorback]
re_normal = re.compile(normal.replace("[", r"\["))
re_hilite = re.compile("(?:%s)(.*)(?=%s)" % (hilite.replace("[", r"\["), fgstop))
re_uline = re.compile("(?:%s)(.*?)(?=%s)" % (ANSI_UNDERLINE.replace("[",r"\["), fgstop))
re_string = re.compile(r'(?P<htmlchars>[<&>])|(?P<space>^[ \t]+)|(?P<lineend>\r\n|\r|\n)', re.S|re.M|re.I) re_string = re.compile(r'(?P<htmlchars>[<&>])|(?P<space>^[ \t]+)|(?P<lineend>\r\n|\r|\n)', re.S|re.M|re.I)
def re_color(self, text): def re_color(self, text):
"""Replace ansi colors with html color class names. """
Let the client choose how it will display colors, if it wishes to.""" Replace ansi colors with html color class names.
for colorname, code in self.colorcodes: Let the client choose how it will display colors, if it wishes to. """
regexp = "(?:%s)(.*?)(?=%s)" % (code, self.codestop) for colorname, regex in self.re_fgs:
text = re.sub(regexp, r'''<span class="%s">\1</span>''' % colorname, text) text = regex.sub(r'''<span class="%s">\1</span>''' % colorname, text)
return re.sub(self.normalcode, "", text) for bgname, regex in self.re_bgs:
text = regex.sub(r'''<span class="%s">\1</span>''' % bgname, text)
return self.re_normal.sub("", text)
def re_bold(self, text): def re_bold(self, text):
"Clean out superfluous hilights rather than set <strong>to make it match the look of telnet." "Clean out superfluous hilights rather than set <strong>to make it match the look of telnet."
#"Replace ansi hilight with strong text element." return self.re_hilite.sub(r'<strong>\1</strong>', text)
#regexp = "(?:%s)(.*?)(?=%s)" % (self.bold, self.codestop)
#return re.sub(regexp, r'<strong>\1</strong>', text)
return re.sub(self.bold, "", text)
def re_underline(self, text): def re_underline(self, text):
"Replace ansi underline with html underline class name." "Replace ansi underline with html underline class name."
regexp = "(?:%s)(.*?)(?=%s)" % (self.underline, self.codestop) return self.re_uline.sub(r'<span class="underline">\1</span>', text)
return re.sub(regexp, r'<span class="underline">\1</span>', text)
def remove_bells(self, text): def remove_bells(self, text):
"Remove ansi specials" "Remove ansi specials"
@ -99,15 +132,13 @@ class TextToHTMLparser(object):
t = t.replace(' ', '&nbsp;') t = t.replace(' ', '&nbsp;')
return t return t
def parse(self, text): def parse(self, text, strip_ansi=False):
""" """
Main access function, converts a text containing Main access function, converts a text containing
ansi codes into html statements. ansi codes into html statements.
""" """
# parse everything to ansi first # parse everything to ansi first
text = ansi.parse_ansi(text) text = parse_ansi(text, strip_ansi=strip_ansi, xterm256=False)
# convert all ansi to html # convert all ansi to html
result = re.sub(self.re_string, self.do_sub, text) result = re.sub(self.re_string, self.do_sub, text)
result = self.re_color(result) result = self.re_color(result)
@ -118,7 +149,7 @@ class TextToHTMLparser(object):
result = self.remove_backspaces(result) result = self.remove_backspaces(result)
result = self.convert_urls(result) result = self.convert_urls(result)
# clean out eventual ansi that was missed # clean out eventual ansi that was missed
result = ansi.parse_ansi(result, strip_ansi=True) #result = parse_ansi(result, strip_ansi=True)
return result return result
@ -128,8 +159,8 @@ HTML_PARSER = TextToHTMLparser()
# Access function # Access function
# #
def parse_html(string, parser=HTML_PARSER): def parse_html(string, strip_ansi=False, parser=HTML_PARSER):
""" """
Parses a string, replace ansi markup with html Parses a string, replace ansi markup with html
""" """
return parser.parse(string) return parser.parse(string, strip_ansi=strip_ansi)

View file

@ -18,12 +18,17 @@ body {
font-family: 'DejaVu Sans Mono', Consolas, Inconsolata, 'Lucida Console', monospace; font-family: 'DejaVu Sans Mono', Consolas, Inconsolata, 'Lucida Console', monospace;
line-height: 1.6em } line-height: 1.6em }
a:link, a:visited { color: #fff } a:link, a:visited { color: #fff }
a:hover, a:active { color: #ccc } a:hover, a:active { color: #ccc }
/* Set this to e.g. bolder if wanting to have ansi-highlights bolden
* stand-alone text.*/
strong {font-weight:normal;}
/* Base style for new messages in the main message area */ /* Base style for new messages in the main message area */
.msg { .msg {
white-space: pre-wrap; white-space: pre-wrap;
padding: .5em .9em;} padding: .5em .9em;}
/*border-bottom: 1px dotted #222 } /*optional line between messages */ /*border-bottom: 1px dotted #222 } /*optional line between messages */
@ -40,22 +45,41 @@ a:hover, a:active { color: #ccc }
.err { color: #f00 } .err { color: #f00 }
/* Style specific classes corresponding to formatted, narative text. */ /* Style specific classes corresponding to formatted, narative text. */
.white { color: white; }
.cyan { color: #00FFFF; }
.blue { color: blue; }
.red { color: red; } .red { color: red; }
.magenta { color: #FF00FF; }
.lime { color: lime; }
.yellow { color: yellow; }
.gray { color: gray; }
.teal { color: teal; }
.navy { color: navy; }
.maroon { color: maroon; } .maroon { color: maroon; }
.purple { color: purple; } .lime { color: lime; }
.green { color: green; } .green { color: green; }
.yellow { color: yellow; }
.olive { color: olive; } .olive { color: olive; }
.blue { color: blue; }
.navy { color: navy; }
.magenta { color: #FF00FF; }
.purple { color: purple; }
.cyan { color: #00FFFF; }
.teal { color: teal; }
.white { color: white; }
.gray { color: gray; }
.dimgray {color: #696969;}
.black {color: black;}
.underline { text-decoration: underline; } .underline { text-decoration: underline; }
.bgred { background-color: red;}
.bgmaroon { background-color: maroon;}
.bglime { background-color: lime;}
.bggreen { background-color: green;}
.bgyellow { background-color: yellow;}
.bgolive { background-color: olive;}
.bgblue { background-color: blue;}
.bgnavy { background-color: navy;}
.bgmagenta { background-color: #FF00FF;}
.bgpurple { background-color: purple;}
.bgcyan { background-color: #00FFFF;}
.bgteal { background-color: teal;}
.bgwhite { background-color: white;}
.bggray { background-color: gray;}
.bgdimgray { background-color: #696969;}
.bgblack { background-color: black;}
/* Container surrounding entire chat */ /* Container surrounding entire chat */
#wrapper { #wrapper {
position: relative; position: relative;