Merge pull request #2008 from friarzen/webclient_options2

Extend the Webclient Options handling
This commit is contained in:
Griatch 2020-01-28 19:26:11 +01:00 committed by GitHub
commit de6ef73949
10 changed files with 537 additions and 2 deletions

View file

@ -88,6 +88,10 @@ div {margin:0px;}
height: 100%;
}
.card {
background-color: #333;
}
/* Container surrounding entire client */
#clientwrapper {
height: 100%;

View file

@ -0,0 +1,6 @@
@font-face {
font-family: 'DejaVu Sans Mono';
src: url('/static/webclient/fonts/DejaVuSansMono-webfont.woff') format('woff');
font-weight: normal;
font-style: normal;
}

View file

@ -0,0 +1,25 @@
/*
*
* Evennia Webclient help plugin
*
*/
let clienthelp_plugin = (function () {
//
//
//
var onOptionsUI = function (parentdiv) {
var help_text = $( [
"<div style='font-weight: bold;'>",
"<a href='http://evennia.com'>Evennia</a>",
" Webclient Settings:",
"</div>"
].join(""));
parentdiv.append(help_text);
}
return {
init: function () {},
onOptionsUI: onOptionsUI,
}
})();
window.plugin_handler.add("clienthelp", clienthelp_plugin);

View file

@ -0,0 +1,79 @@
/*
*
* Evennia Webclient default "send-text-on-enter-key" IO plugin
*
*/
let font_plugin = (function () {
const font_urls = {
'B612 Mono': 'https://fonts.googleapis.com/css?family=B612+Mono&display=swap',
'Consolas': 'https://fonts.googleapis.com/css?family=Consolas&display=swap',
'DejaVu Sans Mono': '/static/webclient/fonts/DejaVuSansMono.css',
'Fira Mono': 'https://fonts.googleapis.com/css?family=Fira+Mono&display=swap',
'Inconsolata': 'https://fonts.googleapis.com/css?family=Inconsolata&display=swap',
'Monospace': '',
'Roboto Mono': 'https://fonts.googleapis.com/css?family=Roboto+Mono&display=swap',
'Source Code Pro': 'https://fonts.googleapis.com/css?family=Source+Code+Pro&display=swap',
'Ubuntu Mono': 'https://fonts.googleapis.com/css?family=Ubuntu+Mono&display=swap',
};
//
//
//
var onOptionsUI = function (parentdiv) {
var fontselect = $('<select>');
var sizeselect = $('<select>');
var fonts = Object.keys(font_urls);
for (var x = 0; x < fonts.length; x++) {
var option = $('<option value="'+fonts[x]+'">'+fonts[x]+'</option>');
fontselect.append(option);
}
for (var x = 4; x < 21; x++) {
var val = (x/10.0);
var option = $('<option value="'+val+'">'+x+'</option>');
sizeselect.append(option);
}
fontselect.val('DejaVu Sans Mono'); // default value
sizeselect.val('0.9'); // default scaling factor
// font-family change callback
fontselect.on('change', function () {
$(document.body).css('font-family', $(this).val());
});
// font size change callback
sizeselect.on('change', function () {
$(document.body).css('font-size', $(this).val()+"em");
});
// add the font selection dialog control to our parentdiv
parentdiv.append('<div style="font-weight: bold">Font Selection:</div>');
parentdiv.append(fontselect);
parentdiv.append(sizeselect);
}
//
// Font plugin init function (adds the urls for the webfonts to the page)
//
var init = function () {
var head = $(document.head);
var fonts = Object.keys(font_urls);
for (var x = 0; x < fonts.length; x++) {
if ( fonts[x] != "Monospace" ) {
var url = font_urls[ fonts[x] ];
var link = $('<link href="'+url+'" rel="stylesheet">');
head.append( link );
}
}
}
return {
init: init,
onOptionsUI: onOptionsUI,
}
})();
window.plugin_handler.add("font", font_plugin);

View file

@ -383,6 +383,14 @@ let goldenlayout = (function () {
// Public
//
//
// helper accessor for other plugins to add new known-message types
var addKnownType = function (newtype) {
if( knownTypes.includes(newtype) == false ) {
knownTypes.push(newtype);
}
}
//
//
@ -526,7 +534,7 @@ let goldenlayout = (function () {
onKeydown: onKeydown,
onText: onText,
getGL: function () { return myLayout; },
addKnownType: function (newtype) { knownTypes.push(newtype); },
addKnownType: addKnownType,
}
}());
window.plugin_handler.add("goldenlayout", goldenlayout);

View file

@ -0,0 +1,68 @@
/*
* IFrame plugin
* REQUIRES: goldenlayout.js
*/
let iframe = (function () {
var url = window.location.origin;
//
// Create iframe component
var createIframeComponent = function () {
var myLayout = window.plugins["goldenlayout"].getGL();
myLayout.registerComponent( "iframe", function (container, componentState) {
// build the iframe
var div = $('<iframe src="' + url + '">');
div.css("width", "100%");
div.css("height", "inherit");
div.appendTo( container.getElement() );
});
}
// handler for the "iframe" button
var onOpenIframe = function () {
var iframeComponent = {
title: url,
type: "component",
componentName: "iframe",
componentState: {
},
};
// Create a new GoldenLayout tab filled with the iframeComponent above
var myLayout = window.plugins["goldenlayout"].getGL();
var main = myLayout.root.getItemsByType("stack")[0].getActiveContentItem();
main.parent.addChild( iframeComponent );
}
// Public
var onOptionsUI = function (parentdiv) {
var iframebutton = $('<input type="button" value="Open Game Website" />');
iframebutton.on('click', onOpenIframe);
parentdiv.append( '<div style="font-weight: bold">Restricted Browser-in-Browser:</div>' );
parentdiv.append( iframebutton );
}
//
//
var postInit = function() {
// Are we using GoldenLayout?
if( window.plugins["goldenlayout"] ) {
createIframeComponent();
$("#iframebutton").bind("click", onOpenIframe);
}
console.log('IFrame plugin Loaded');
}
return {
init: function () {},
postInit: postInit,
onOptionsUI: onOptionsUI,
}
})();
window.plugin_handler.add("iframe", iframe);

View file

@ -0,0 +1,154 @@
/*
* Spawns plugin
* REQUIRES: goldenlayout.js
*/
let spawns = (function () {
var ignoreDefaultKeydown = false;
var spawnmap = {}; // { id1: { r:regex, t:tag } } pseudo-array of regex-tag pairs
//
// changes the spawnmap row's contents to the new regex/tag provided,
// this avoids leaving stale regex/tag definitions in the spawnmap
var onAlterTag = function (evnt) {
var adult = $(evnt.target).parent();
var children = adult.children();
var id = $(adult).data('id');
var regex = $(children[0]).val();// spaces before/after are valid regex syntax, unfortunately
var mytag = $(children[1]).val().trim();
if( mytag != "" && regex != "" ) {
if( !(id in spawnmap) ) {
spawnmap[id] = {};
}
spawnmap[id]["r"] = regex;
spawnmap[id]["t"] = mytag;
localStorage.setItem( "evenniaMessageRoutingSavedState", JSON.stringify(spawnmap) );
window.plugins["goldenlayout"].addKnownType( mytag );
}
}
//
// deletes the entire regex/tag/delete button row.
var onDeleteTag = function (evnt) {
var adult = $(evnt.target).parent();
var children = adult.children();
var id = $(adult).data('id');
delete spawnmap[id];
localStorage.setItem( "evenniaMessageRoutingSavedState", JSON.stringify(spawnmap) );
adult.remove(); // remove this set of input boxes/etc from the DOM
}
//
var onFocusIn = function (evnt) {
ignoreDefaultKeydown = true;
}
//
var onFocusOut = function (evnt) {
ignoreDefaultKeydown = false;
onAlterTag(evnt); // percolate event so closing the pane, etc saves any last changes.
}
//
// display a row with proper editting hooks
var displayRow = function (formdiv, div, regexstring, tagstring) {
var regex = $('<input class="regex" type=text value="'+regexstring+'"/>');
var tag = $('<input class="tag" type=text value="'+tagstring+'"/>');
var del = $('<input class="delete-regex" type=button value="X"/>');
regex.on('change', onAlterTag );
regex.on('focusin', onFocusIn );
regex.on('focusout', onFocusOut );
tag.on('change', onAlterTag );
tag.on('focusin', onFocusIn );
tag.on('focusout', onFocusOut );
del.on('click', onDeleteTag );
div.append(regex);
div.append(tag);
div.append(del);
formdiv.append(div);
}
//
// generate a whole new regex/tag/delete button row
var onNewRegexRow = function (formdiv) {
var nextid = 1;
while( nextid in spawnmap ) { // pseudo-index spawnmap with id reuse
nextid++;
}
var div = $("<div data-id='"+nextid+"'>");
displayRow(formdiv, div, "", "");
}
// Public
//
// onOptionsUI -- display the existing spawnmap and a button to create more entries.
//
var onOptionsUI = function (parentdiv) {
var formdiv = $('<div>');
var button= $('<input type="button" value="New Regex/Tag Pair" />');
button.on('click', function () { onNewRegexRow(formdiv) });
formdiv.append(button);
// display the existing spawnmap
for( var id in spawnmap ) {
var div = $("<div data-id='"+id+"'>");
displayRow(formdiv, div, spawnmap[id]["r"], spawnmap[id]["t"] );
}
parentdiv.append('<div style="font-weight: bold">Message Routing:</div>');
parentdiv.append(formdiv);
}
//
// onText -- catch Text before it is routed by the goldenlayout router
// then test our list of regexes on the given text to see if it matches.
// If it does, rewrite the Text Type to be our tag value instead.
//
var onText = function (args, kwargs) {
var div = $("<div>" + args[0] + "</div>");
var txt = div.text();
for( var id in spawnmap ) {
var regex = spawnmap[id]["r"];
if ( txt.match(regex) != null ) {
kwargs['type'] = spawnmap[id]["t"];
}
}
return false;
}
//
// OnKeydown -- if the Options window is open, capture focus
//
var onKeydown = function(evnt) {
return ignoreDefaultKeydown;
}
//
// init
//
var init = function () {
var ls_spawnmap = localStorage.getItem( "evenniaMessageRoutingSavedState" );
if( ls_spawnmap ) {
spawnmap = JSON.parse(ls_spawnmap);
for( var id in spawnmap ) {
window.plugins["goldenlayout"].addKnownType( spawnmap[id]["t"] );
}
}
console.log('Client-Side Message Routing plugin initialized');
}
return {
init: init,
onOptionsUI: onOptionsUI,
onText: onText,
onKeydown: onKeydown,
}
})();
window.plugin_handler.add("spawns", spawns);

View file

@ -0,0 +1,184 @@
/*
* Options 2.0
* REQUIRES: goldenlayout.js
*/
let options2 = (function () {
var options_container = null ;
//
// When the user changes a setting from the interface
var onOptionCheckboxChanged = function (evnt) {
var name = $(evnt.target).data("setting");
var value = $(evnt.target).is(":checked");
options[name] = value;
Evennia.msg("webclient_options", [], options);
}
//
// Callback to display our basic OptionsUI
var onOptionsUI = function (parentdiv) {
var checked;
checked = options["gagprompt"] ? "checked='checked'" : "";
var gagprompt = $( [ "<label>",
"<input type='checkbox' data-setting='gagprompt' " + checked + "'>",
" Don't echo prompts to the main text area",
"</label>"
].join("") );
checked = options["notification_popup"] ? "checked='checked'" : "";
var notifypopup = $( [ "<label>",
"<input type='checkbox' data-setting='notification_popup' " + checked + "'>",
" Popup notification",
"</label>"
].join("") );
checked = options["notification_sound"] ? "checked='checked'" : "";
var notifysound = $( [ "<label>",
"<input type='checkbox' data-setting='notification_sound' " + checked + "'>",
" Play a sound",
"</label>"
].join("") );
gagprompt.on("change", onOptionCheckboxChanged);
notifypopup.on("change", onOptionCheckboxChanged);
notifysound.on("change", onOptionCheckboxChanged);
parentdiv.append(gagprompt);
parentdiv.append(notifypopup);
parentdiv.append(notifysound);
}
//
// Create and register the "options" golden-layout component
var createOptionsComponent = function () {
var myLayout = window.plugins["goldenlayout"].getGL();
myLayout.registerComponent( "options", function (container, componentState) {
var plugins = window.plugins;
options_container = container.getElement();
// build the buttons
var div = $("<div class='accordion' style='overflow-y:scroll; height:inherit;'>");
for( let plugin in plugins ) {
if( "onOptionsUI" in plugins[plugin] ) {
var card = $("<div class='card'>");
var body = $("<div>");
plugins[plugin].onOptionsUI( body );
card.append(body);
card.appendTo( div );
}
}
div.appendTo( options_container );
});
}
// handler for the "Options" button
var onOpenCloseOptions = function () {
var optionsComponent = {
title: "Options",
type: "component",
componentName: "options",
componentState: {
},
};
// Create a new GoldenLayout tab filled with the optionsComponent above
var myLayout = window.plugins["goldenlayout"].getGL();
if( ! options_container ) {
// open new optionsComponent
var main = myLayout.root.getItemsByType("stack")[0].getActiveContentItem();
myLayout.on( "tabCreated", function( tab ) {
if( tab.contentItem.componentName == "options" ) {
tab
.closeElement
.off("click")
.click( function () {
options_container = null;
tab.contentItem.remove();
});
options_container = tab.contentItem;
}
});
main.parent.addChild( optionsComponent );
} else {
options_container.remove();
options_container = null;
}
}
// Public
//
// Called when options settings are sent from server
var onGotOptions = function (args, kwargs) {
var addKnownType = window.plugins["goldenlayout"].addKnownType;
$.each(kwargs, function(key, value) {
options[key] = value;
// for "available_server_tags", addKnownType for each value ["tag1", "tag2", ... ]
if( (key === "available_server_tags") && addKnownType ) {
$.each( value, addKnownType );
}
});
}
//
// Called when the user logged in
var onLoggedIn = function (args, kwargs) {
Evennia.msg("webclient_options", [], {});
}
//
// Display a "prompt" command from the server
var onPrompt = function (args, kwargs) {
// display the prompt in the output window if gagging is disabled
if( options["gagprompt"] == false ) {
plugin_handler.onText(args, kwargs);
}
// don't claim this Prompt as completed.
return false;
}
//
//
var init = function() {
var optionsbutton = $("<button id='optionsbutton'>&#x2699;</button>");
$("#toolbar").append( optionsbutton );
options["gagprompt"] = true;
options["notification_popup"] = true;
options["notification_sound"] = true;
}
//
//
var postInit = function() {
// Are we using GoldenLayout?
if( window.plugins["goldenlayout"] ) {
createOptionsComponent();
$("#optionsbutton").bind("click", onOpenCloseOptions);
}
console.log("Options 2.0 Loaded");
}
return {
init: init,
postInit: postInit,
onGotOptions: onGotOptions,
onLoggedIn: onLoggedIn,
onOptionsUI: onOptionsUI,
onPrompt: onPrompt,
}
})();
window.plugin_handler.add("options2", options2);

View file

@ -76,9 +76,16 @@ JQuery available.
{% block guilib_import %}
<script src={% static "webclient/js/webclient_gui.js" %} language="javascript" type="text/javascript" charset="utf-8"></script>
<script src={% static "webclient/js/plugins/goldenlayout_default_config.js" %} type="text/javascript"></script>
<script src={% static "webclient/js/plugins/clienthelp.js" %} language="javascript" type="text/javascript" charset="utf-8"></script>
<script src={% static "webclient/js/plugins/popups.js" %} language="javascript" type="text/javascript"></script>
<script src={% static "webclient/js/plugins/options.js" %} language="javascript" type="text/javascript"></script>
<!--
<script src={% static "webclient/js/plugins/options.js" %} language="javascript" type="text/javascript"></script>
-->
<script src={% static "webclient/js/plugins/options2.js" %} language="javascript" type="text/javascript"></script>
<script src={% static "webclient/js/plugins/iframe.js" %} language="javascript" type="text/javascript"></script>
<script src={% static "webclient/js/plugins/message_routing.js" %} language="javascript" type="text/javascript"></script>
<script src={% static "webclient/js/plugins/history.js" %} language="javascript" type="text/javascript"></script>
<script src={% static "webclient/js/plugins/font.js" %} language="javascript" type="text/javascript" charset="utf-8"></script>
<!--
<script src={% static "webclient/js/plugins/splithandler.js" %} language="javascript" type="text/javascript"></script>
-->