mirror of
https://github.com/evennia/evennia.git
synced 2026-03-16 21:06:30 +01:00
Add a simple raw HTML message renderer
This commit is contained in:
parent
a1427fdb84
commit
922e75ebb0
3 changed files with 413 additions and 0 deletions
63
evennia/web/static/webclient/js/plugins/html.js
Normal file
63
evennia/web/static/webclient/js/plugins/html.js
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* Evennia server-side-generated "raw" HTML message handler plugin
|
||||
*
|
||||
* PLUGIN ORDER PREREQS:
|
||||
* loaded after:
|
||||
* webclient_gui.js
|
||||
* option2.js
|
||||
* loaded before:
|
||||
*
|
||||
*
|
||||
* To use, at minimum, in evennia python code:
|
||||
* target.msg( html="<div><span>...etc...</span></div>" )
|
||||
*
|
||||
* or, if you prefer tagged routing (RECOMMENDED):
|
||||
* target.msg( html=("<div><span>...etc...</span></div>",{'type':'tag'}) )
|
||||
*
|
||||
*/
|
||||
let html_plugin = (function () {
|
||||
//
|
||||
var html = function (args, kwargs) {
|
||||
let options = window.options;
|
||||
if( !("html" in options) || options["html"] === false ) { return; }
|
||||
|
||||
var mwins = window.plugins["goldenlayout"].routeMessage(args, kwargs);
|
||||
mwins.forEach( function (mwin) {
|
||||
mwin.append(args[0]);
|
||||
mwin.scrollTop(mwin[0].scrollHeight);
|
||||
});
|
||||
}
|
||||
|
||||
//
|
||||
var onOptionsUI = function (parentdiv) {
|
||||
let options = window.options;
|
||||
var checked;
|
||||
|
||||
checked = options["html"] ? "checked='checked'" : "";
|
||||
var mmHtml = $( [ "<label>",
|
||||
"<input type='checkbox' data-setting='html' " + checked + "'>",
|
||||
" Prefer server-side generated direct-HTML messages over old-school ASCII text",
|
||||
"</label>"
|
||||
].join("") );
|
||||
mmHtml.on("change", window.plugins["options2"].onOptionCheckboxChanged);
|
||||
|
||||
parentdiv.append(mmHtml);
|
||||
}
|
||||
|
||||
//
|
||||
// Mandatory plugin init function
|
||||
var init = function () {
|
||||
let options = window.options;
|
||||
options["html"] = true;
|
||||
|
||||
let Evennia = window.Evennia;
|
||||
Evennia.emitter.on("html", html); // capture "image" commands
|
||||
console.log('HTML plugin initialized');
|
||||
}
|
||||
|
||||
return {
|
||||
init: init,
|
||||
onOptionsUI: onOptionsUI,
|
||||
}
|
||||
})();
|
||||
plugin_handler.add("html_plugin", html_plugin);
|
||||
348
evennia/web/static/webclient/js/plugins/text2html.js
Normal file
348
evennia/web/static/webclient/js/plugins/text2html.js
Normal file
|
|
@ -0,0 +1,348 @@
|
|||
|
||||
/*
|
||||
*
|
||||
* Evennia Webclient text2html component
|
||||
*
|
||||
* This is used in conjunction with the main evennia.js library, which
|
||||
* handles all the communication with the Server.
|
||||
*
|
||||
*/
|
||||
|
||||
//
|
||||
// Global Parser for text2html
|
||||
//
|
||||
var text2html_plugin = (function () {
|
||||
"use strict"
|
||||
|
||||
let asciiESC = String.fromCharCode(27); // string literal for ASCII ESC bytes
|
||||
|
||||
let foreground = "color-102"; // state tracker for foreground css
|
||||
let background = ""; // state tracker for background css
|
||||
let underline = ""; // state tracker for underlines css
|
||||
|
||||
let pipecodes = /^(\|\[?[0-5][0-5][0-5])|(\|\[[0-9][0-9]?m)|(\|\[?=[a-z])|(\|[![]?[unrgybmcwxhRGYBMCWXH_/>*^-])/;
|
||||
// example ^|000 or ^|[22m or ^|=a or ^|_
|
||||
|
||||
let csslookup = {
|
||||
"|n": "normal",
|
||||
"|r": "color-009",
|
||||
"|g": "color-010",
|
||||
"|y": "color-011",
|
||||
"|b": "color-012",
|
||||
"|m": "color-013",
|
||||
"|c": "color-014",
|
||||
"|w": "color-015",
|
||||
"|x": "color-008",
|
||||
"|R": "color-001",
|
||||
"|G": "color-002",
|
||||
"|Y": "color-003",
|
||||
"|B": "color-004",
|
||||
"|M": "color-005",
|
||||
"|C": "color-006",
|
||||
"|W": "color-007",
|
||||
"|X": "color-000",
|
||||
"|[r": "bgcolor-196",
|
||||
"|[g": "bgcolor-046",
|
||||
"|[y": "bgcolor-226",
|
||||
"|[b": "bgcolor-021",
|
||||
"|[m": "bgcolor-201",
|
||||
"|[c": "bgcolor-051",
|
||||
"|[w": "bgcolor-231",
|
||||
"|[x": "bgcolor-102",
|
||||
"|[R": "bgcolor-001",
|
||||
"|[G": "bgcolor-002",
|
||||
"|[Y": "bgcolor-003",
|
||||
"|[B": "bgcolor-004",
|
||||
"|[M": "bgcolor-005",
|
||||
"|[C": "bgcolor-006",
|
||||
"|[W": "bgcolor-007",
|
||||
"|[X": "bgcolor-000",
|
||||
"|=a": "color-016",
|
||||
"|=b": "color-232",
|
||||
"|=c": "color-233",
|
||||
"|=d": "color-234",
|
||||
"|=e": "color-235",
|
||||
"|=f": "color-236",
|
||||
"|=g": "color-237",
|
||||
"|=h": "color-238",
|
||||
"|=i": "color-239",
|
||||
"|=j": "color-240",
|
||||
"|=k": "color-241",
|
||||
"|=l": "color-242",
|
||||
"|=m": "color-243",
|
||||
"|=n": "color-244",
|
||||
"|=o": "color-245",
|
||||
"|=p": "color-246",
|
||||
"|=q": "color-247",
|
||||
"|=r": "color-248",
|
||||
"|=s": "color-249",
|
||||
"|=t": "color-250",
|
||||
"|=u": "color-251",
|
||||
"|=v": "color-252",
|
||||
"|=w": "color-253",
|
||||
"|=x": "color-254",
|
||||
"|=y": "color-255",
|
||||
"|=z": "color-231",
|
||||
"|[=a": "bgcolor-016",
|
||||
"|[=b": "bgcolor-232",
|
||||
"|[=c": "bgcolor-233",
|
||||
"|[=d": "bgcolor-234",
|
||||
"|[=e": "bgcolor-235",
|
||||
"|[=f": "bgcolor-236",
|
||||
"|[=g": "bgcolor-237",
|
||||
"|[=h": "bgcolor-238",
|
||||
"|[=i": "bgcolor-239",
|
||||
"|[=j": "bgcolor-240",
|
||||
"|[=k": "bgcolor-241",
|
||||
"|[=l": "bgcolor-242",
|
||||
"|[=m": "bgcolor-243",
|
||||
"|[=n": "bgcolor-244",
|
||||
"|[=o": "bgcolor-245",
|
||||
"|[=p": "bgcolor-246",
|
||||
"|[=q": "bgcolor-247",
|
||||
"|[=r": "bgcolor-248",
|
||||
"|[=s": "bgcolor-249",
|
||||
"|[=t": "bgcolor-250",
|
||||
"|[=u": "bgcolor-251",
|
||||
"|[=v": "bgcolor-252",
|
||||
"|[=w": "bgcolor-253",
|
||||
"|[=x": "bgcolor-254",
|
||||
"|[=y": "bgcolor-255",
|
||||
"|[=z": "bgcolor-231",
|
||||
// not sure what these nexts ones are actually supposed to map to
|
||||
"|[0m": "normal",
|
||||
"|[1m": "normal",
|
||||
"|[22m": "normal",
|
||||
"|[36m": "color-006",
|
||||
"|[37m": "color-015",
|
||||
}
|
||||
|
||||
function ascii (l) {
|
||||
let a = new String(l); // force string
|
||||
return a.charCodeAt(0);
|
||||
}
|
||||
|
||||
// dumb convert any leading or trailing spaces/tabs to sequences
|
||||
var handleSpaces = function (text) {
|
||||
// TODO should probably get smart about replacing spaces inside "normal" text
|
||||
return text.replace( /\t/g, " ").replace( / /g, " ");
|
||||
}
|
||||
|
||||
|
||||
// javascript doesn't have a native sprintf-like function
|
||||
function zfill(string, size) {
|
||||
while (string.length < size) string = "0" + string;
|
||||
return string;
|
||||
}
|
||||
|
||||
|
||||
// given string starting with a pipecode |xx
|
||||
// return the css (if any) and text remainder
|
||||
var pipe2css = function(pipecode) {
|
||||
let regx = "";
|
||||
let css = "color-102";
|
||||
|
||||
regx = /^(\|\[?[nrgybmcwxhRGYBMCWX])/;
|
||||
css = pipecode.match( regx );
|
||||
if( css != null ) {
|
||||
return csslookup[ css[1] ];
|
||||
}
|
||||
|
||||
regx = /^(\|\[?=[a-z])/;
|
||||
css = pipecode.match( regx );
|
||||
if( css != null ) {
|
||||
return csslookup[ css[1] ];
|
||||
}
|
||||
|
||||
regx = /^(\|\[[0-9][0-9]?m)/;
|
||||
css = pipecode.match( regx );
|
||||
if( css != null ) {
|
||||
return csslookup[ css[1] ];
|
||||
}
|
||||
|
||||
regx = /^(\|n)/;
|
||||
css = pipecode.match( regx );
|
||||
if( css != null ) {
|
||||
return "normal";
|
||||
}
|
||||
|
||||
regx = /^(\|u)/;
|
||||
css = pipecode.match( regx );
|
||||
if( css != null ) {
|
||||
return "underline";
|
||||
}
|
||||
|
||||
regx = /^\|([0-5][0-5][0-5])/;
|
||||
css = pipecode.match( regx );
|
||||
if( css != null ) {
|
||||
return "color-" + zfill( (parseInt(css[1], 6) + 16).toString(), 3);
|
||||
}
|
||||
|
||||
regx = /^\|\[([0-5][0-5][0-5])/;
|
||||
css = pipecode.match( regx );
|
||||
if( css != null ) {
|
||||
return "bgcolor-" + zfill( (parseInt(css[1], 6) + 16).toString(), 3);
|
||||
}
|
||||
|
||||
return css;
|
||||
}
|
||||
|
||||
|
||||
// convert any HTML sensitive characters to &code; format
|
||||
var htmlEscape = function (text) {
|
||||
text = text.replace(/&/g, "&");
|
||||
text = text.replace(/</g, "<");
|
||||
text = text.replace(/>/g, ">");
|
||||
text = text.replace(/"/g, """);
|
||||
text = text.replace(/'/g, "'");
|
||||
text = handleSpaces(text);
|
||||
return text;
|
||||
}
|
||||
|
||||
|
||||
// track stateful CSS
|
||||
var trackCSS = function (css) {
|
||||
if( (typeof css !== 'string') && ! (css instanceof String) ) {
|
||||
css = "";
|
||||
}
|
||||
|
||||
if( css.startsWith( "color-" ) ) {
|
||||
foreground = css;
|
||||
}
|
||||
|
||||
if( css.startsWith( "bgcolor-" ) ) {
|
||||
background = css;
|
||||
}
|
||||
|
||||
if( css === "underline" ) {
|
||||
underline = css;
|
||||
}
|
||||
|
||||
if( css === "normal" ) {
|
||||
foreground = "color-102";
|
||||
background = "";
|
||||
underline = "";
|
||||
}
|
||||
|
||||
return foreground + " " + background + " " + underline ;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
var parse2HTML = function (text) {
|
||||
let html = "";
|
||||
foreground = "color-102"; // state tracker for foreground css
|
||||
background = ""; // state tracker for background css
|
||||
underline = ""; // state tracker for underlines css
|
||||
|
||||
// HACK: parse TELNET ASCII byte-by-byte, convert ESC's to |'s -- serverside "raw" bug?
|
||||
// Bug is further proven out by the fact that |'s don't come through as doubles.
|
||||
let hack = new RegExp( String.fromCharCode(27) );
|
||||
if( text.match( hack ) ) {
|
||||
let chars = text.split(''); // to characters
|
||||
for( let n=0; n<chars.length; n++ ) {
|
||||
if( chars[n] === '|' ) {
|
||||
console.log( 'Got Pipe' );
|
||||
chars[n] = "|";
|
||||
}
|
||||
if( ascii(chars[n]) === 27 ) {
|
||||
chars[n] = '|';
|
||||
}
|
||||
}
|
||||
text = chars.join(''); // from character strings
|
||||
}
|
||||
|
||||
let strings = text.split( /(\n|\|[\/])/ ); // newline or pipe-slash
|
||||
for( let x=0; x<strings.length; x++ ) {
|
||||
// hack double pipes -- convert them temporarily to HTML -- |
|
||||
var string = strings[x].replace( /\|\|/g, "|" );
|
||||
|
||||
// special case for blank lines, avoid <div>'s with no HTML height
|
||||
if( string === "" ) {
|
||||
html += "<div> </div>";
|
||||
continue;
|
||||
}
|
||||
|
||||
html += "<div>";
|
||||
|
||||
let spans = string.split( "|" );
|
||||
|
||||
// this split means there are 2 possible cases
|
||||
|
||||
// possibly-0-length leading span with "default" styling (may be the ONLY span)
|
||||
let span = spans[0].replaceAll("|", "|"); // avoid htmlEscape mangling literal |'s
|
||||
if( span.length > 0 ) {
|
||||
html += "<span class='color-102'>" + htmlEscape(span) + "</span>";
|
||||
}
|
||||
|
||||
// "standard" span array of [xxxtext1, xxtext2, xxtext3], where x's are pipe-codes
|
||||
for( let n=1; n<spans.length; n++ ) {
|
||||
span = "|" + spans[n].replaceAll("|", "|"); // avoid htmlEscape mangling |'s
|
||||
let pipecode = "";
|
||||
let remainder = span;
|
||||
|
||||
let tags = span.match( pipecodes ); // match against large pipecode regex
|
||||
if( tags != null ) {
|
||||
pipecode = tags[0]; // matched text is the pipecode
|
||||
remainder = span.replace( pipecode, "" ); // everything but the pipecode
|
||||
}
|
||||
|
||||
let css = trackCSS( pipe2css( pipecode ) ); // find css associated with pipe-code
|
||||
html += "<span class='" + css + "'>" + htmlEscape(remainder) + "</span>";
|
||||
}
|
||||
|
||||
html += "</div>";
|
||||
}
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
// The Main text2html message capture fuction -- calls parse2HTML()
|
||||
var text2html = function (args, kwargs) {
|
||||
let options = window.options;
|
||||
if( !("text2html" in options) || options["text2html"] === false ) { return; }
|
||||
|
||||
var mwins = window.plugins["goldenlayout"].routeMessage(args, kwargs);
|
||||
mwins.forEach( function (mwin) {
|
||||
mwin.append( parse2HTML( args[0]) ); // the heavy workload
|
||||
mwin.scrollTop(mwin[0].scrollHeight);
|
||||
});
|
||||
}
|
||||
|
||||
//
|
||||
var onOptionsUI = function (parentdiv) {
|
||||
let options = window.options;
|
||||
var checked;
|
||||
|
||||
checked = options["text2html"] ? "checked='checked'" : "";
|
||||
var text2html = $( [ "<label>",
|
||||
"<input type='checkbox' data-setting='text2html' " + checked + "'>",
|
||||
" Enable client-side Evennia ASCII message rendering",
|
||||
"</label>"
|
||||
].join("") );
|
||||
text2html.on("change", window.plugins["options2"].onOptionCheckboxChanged);
|
||||
|
||||
parentdiv.append(text2html);
|
||||
}
|
||||
|
||||
//
|
||||
// Mandatory plugin init function
|
||||
var init = function () {
|
||||
let options = window.options;
|
||||
options["text2html"] = true;
|
||||
|
||||
let Evennia = window.Evennia;
|
||||
Evennia.emitter.on("text2html", text2html); // capture "text2html" outfunc events
|
||||
|
||||
console.log('Text2Html plugin initialized');
|
||||
}
|
||||
|
||||
return {
|
||||
init: init,
|
||||
onOptionsUI: onOptionsUI,
|
||||
parse2HTML: parse2HTML,
|
||||
}
|
||||
})();
|
||||
plugin_handler.add("text2html_plugin", text2html_plugin);
|
||||
|
|
@ -100,6 +100,8 @@ JQuery available.
|
|||
<script src={% static "webclient/js/plugins/default_in.js" %} language="javascript" type="text/javascript"></script>
|
||||
<script src={% static "webclient/js/plugins/default_out.js" %} language="javascript" type="text/javascript"></script>
|
||||
<script src={% static "webclient/js/plugins/multimedia.js" %} language="javascript" type="text/javascript"></script>
|
||||
<script src={% static "webclient/js/plugins/html.js" %} language="javascript" type="text/javascript"></script>
|
||||
<script src={% static "webclient/js/plugins/text2html.js" %} language="javascript" type="text/javascript"></script>
|
||||
{% endblock %}
|
||||
|
||||
<script src="https://cdn.rawgit.com/ejci/favico.js/master/favico-0.3.10.min.js" language="javascript" type="text/javascript" charset="utf-8"></script>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue