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.CmdSay())
self.add(general.CmdAccess())
self.add(general.CmdColorTest())
# The help system
self.add(help.CmdHelp())

View file

@ -13,7 +13,7 @@ from src.commands.default.muxcommand import MuxCommand, MuxCommandOOC
__all__ = ("CmdHome", "CmdLook", "CmdPassword", "CmdNick",
"CmdInventory", "CmdGet", "CmdDrop", "CmdQuit", "CmdWho",
"CmdSay", "CmdPose", "CmdEncoding", "CmdAccess",
"CmdOOCLook", "CmdIC", "CmdOOC")
"CmdOOCLook", "CmdIC", "CmdOOC", "CmdColorTest")
AT_SEARCH_RESULT = utils.variable_from_module(*settings.SEARCH_AT_RESULT.rsplit('.', 1))
BASE_PLAYER_TYPECLASS = settings.BASE_PLAYER_TYPECLASS
@ -753,3 +753,61 @@ class CmdOOC(MuxCommandOOC):
caller.msg("\n{GYou go OOC.{n\n")
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)
caller.msg(string)
# class CmdPs(MuxCommand):
# """
# list processes

View file

@ -243,7 +243,7 @@ class WebClientSession(session.Session):
if raw:
self.client.lineSend(self.suid, string)
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
except Exception, e:
logger.log_trace()

View file

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

View file

@ -11,7 +11,7 @@ snippet #577349 on http://code.activestate.com.
import re
import cgi
from src.utils import ansi
from ansi import *
class TextToHTMLparser(object):
"""
@ -20,47 +20,80 @@ class TextToHTMLparser(object):
tabstop = 4
# mapping html color name <-> ansi code.
# note that \[ is used here since they go into regexes.
colorcodes = [('white', '\033\[1m\033\[37m'),
('cyan', '\033\[1m\033\[36m'),
('blue', '\033\[1m\033\[34m'),
('red', '\033\[1m\033\[31m'),
('magenta', '\033\[1m\033\[35m'),
('lime', '\033\[1m\033\[32m'),
('yellow', '\033\[1m\033\[33m'),
('gray', '\033\[37m'),
('teal', '\033\[36m'),
('navy', '\033\[34m'),
('maroon', '\033\[31m'),
('purple', '\033\[35m'),
('green', '\033\[32m'),
('olive', '\033\[33m')]
normalcode = '\033\[0m'
bold = '\033\[1m'
underline = '\033\[4m'
codestop = "|".join(co[1] for co in colorcodes + [("", normalcode), ("", bold), ("", underline), ("", "$")])
hilite = ANSI_HILITE
normal = ANSI_NORMAL
underline = ANSI_UNDERLINE
colorcodes = [
('red', hilite + ANSI_RED),
('maroon', ANSI_RED),
('lime', hilite + ANSI_GREEN),
('green', ANSI_GREEN),
('yellow', hilite + ANSI_YELLOW),
('olive', ANSI_YELLOW),
('blue', hilite + ANSI_BLUE),
('navy', ANSI_BLUE),
('magenta', hilite + ANSI_MAGENTA),
('purple', ANSI_MAGENTA),
('cyan', hilite + ANSI_CYAN),
('teal', ANSI_CYAN),
('white', hilite + ANSI_WHITE), # pure white
('gray', ANSI_WHITE), #light grey
('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)
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."""
for colorname, code in self.colorcodes:
regexp = "(?:%s)(.*?)(?=%s)" % (code, self.codestop)
text = re.sub(regexp, r'''<span class="%s">\1</span>''' % colorname, text)
return re.sub(self.normalcode, "", text)
"""
Replace ansi colors with html color class names.
Let the client choose how it will display colors, if it wishes to. """
for colorname, regex in self.re_fgs:
text = regex.sub(r'''<span class="%s">\1</span>''' % colorname, 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):
"Clean out superfluous hilights rather than set <strong>to make it match the look of telnet."
#"Replace ansi hilight with strong text element."
#regexp = "(?:%s)(.*?)(?=%s)" % (self.bold, self.codestop)
#return re.sub(regexp, r'<strong>\1</strong>', text)
return re.sub(self.bold, "", text)
return self.re_hilite.sub(r'<strong>\1</strong>', text)
def re_underline(self, text):
"Replace ansi underline with html underline class name."
regexp = "(?:%s)(.*?)(?=%s)" % (self.underline, self.codestop)
return re.sub(regexp, r'<span class="underline">\1</span>', text)
return self.re_uline.sub(r'<span class="underline">\1</span>', text)
def remove_bells(self, text):
"Remove ansi specials"
@ -99,15 +132,13 @@ class TextToHTMLparser(object):
t = t.replace(' ', '&nbsp;')
return t
def parse(self, text):
def parse(self, text, strip_ansi=False):
"""
Main access function, converts a text containing
ansi codes into html statements.
"""
# 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
result = re.sub(self.re_string, self.do_sub, text)
result = self.re_color(result)
@ -118,7 +149,7 @@ class TextToHTMLparser(object):
result = self.remove_backspaces(result)
result = self.convert_urls(result)
# clean out eventual ansi that was missed
result = ansi.parse_ansi(result, strip_ansi=True)
#result = parse_ansi(result, strip_ansi=True)
return result
@ -128,8 +159,8 @@ HTML_PARSER = TextToHTMLparser()
# 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
"""
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;
line-height: 1.6em }
a:link, a:visited { color: #fff }
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 */
.msg {
white-space: pre-wrap;
white-space: pre-wrap;
padding: .5em .9em;}
/*border-bottom: 1px dotted #222 } /*optional line between messages */
@ -40,22 +45,41 @@ a:hover, a:active { color: #ccc }
.err { color: #f00 }
/* Style specific classes corresponding to formatted, narative text. */
.white { color: white; }
.cyan { color: #00FFFF; }
.blue { color: blue; }
.red { color: red; }
.magenta { color: #FF00FF; }
.lime { color: lime; }
.yellow { color: yellow; }
.gray { color: gray; }
.teal { color: teal; }
.navy { color: navy; }
.maroon { color: maroon; }
.purple { color: purple; }
.lime { color: lime; }
.green { color: green; }
.yellow { color: yellow; }
.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; }
.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 */
#wrapper {
position: relative;