Initial version of code that allows users to interactively select from
different style sheet options. This code is still incomplete and subject to change (e.g. the command line syntax might still change). But it is good enough to demonstrate the concept on simple style sheets (such as selecting between normal and reverse video). git-svn-id: https://shellinabox.googlecode.com/svn/trunk@165 0da03de8-d603-11dd-86c2-0f8696b7b6f9
This commit is contained in:
parent
e78b94961c
commit
7ab3b32465
14 changed files with 742 additions and 54 deletions
|
@ -76,6 +76,8 @@ shellinaboxd_SOURCES = shellinabox/shellinaboxd.c \
|
|||
shellinabox/service.h \
|
||||
shellinabox/session.c \
|
||||
shellinabox/session.h \
|
||||
shellinabox/usercss.c \
|
||||
shellinabox/usercss.h \
|
||||
shellinabox/cgi_root.html \
|
||||
shellinabox/root_page.html \
|
||||
shellinabox/vt100.jspp \
|
||||
|
|
19
Makefile.in
19
Makefile.in
|
@ -73,7 +73,7 @@ PROGRAMS = $(bin_PROGRAMS)
|
|||
am__dirstamp = $(am__leading_dot)dirstamp
|
||||
am_shellinaboxd_OBJECTS = shellinaboxd.$(OBJEXT) \
|
||||
externalfile.$(OBJEXT) launcher.$(OBJEXT) privileges.$(OBJEXT) \
|
||||
service.$(OBJEXT) session.$(OBJEXT) \
|
||||
service.$(OBJEXT) session.$(OBJEXT) usercss.$(OBJEXT) \
|
||||
shellinabox/cgi_root.$(OBJEXT) shellinabox/root_page.$(OBJEXT) \
|
||||
shellinabox/vt100.$(OBJEXT) \
|
||||
shellinabox/shell_in_a_box.$(OBJEXT) \
|
||||
|
@ -320,6 +320,8 @@ shellinaboxd_SOURCES = shellinabox/shellinaboxd.c \
|
|||
shellinabox/service.h \
|
||||
shellinabox/session.c \
|
||||
shellinabox/session.h \
|
||||
shellinabox/usercss.c \
|
||||
shellinabox/usercss.h \
|
||||
shellinabox/cgi_root.html \
|
||||
shellinabox/root_page.html \
|
||||
shellinabox/vt100.jspp \
|
||||
|
@ -503,6 +505,7 @@ distclean-compile:
|
|||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssl.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/trie.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/url.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/usercss.Po@am__quote@
|
||||
|
||||
.c.o:
|
||||
@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
|
||||
|
@ -658,6 +661,20 @@ session.obj: shellinabox/session.c
|
|||
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
|
||||
@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o session.obj `if test -f 'shellinabox/session.c'; then $(CYGPATH_W) 'shellinabox/session.c'; else $(CYGPATH_W) '$(srcdir)/shellinabox/session.c'; fi`
|
||||
|
||||
usercss.o: shellinabox/usercss.c
|
||||
@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT usercss.o -MD -MP -MF $(DEPDIR)/usercss.Tpo -c -o usercss.o `test -f 'shellinabox/usercss.c' || echo '$(srcdir)/'`shellinabox/usercss.c
|
||||
@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/usercss.Tpo $(DEPDIR)/usercss.Po
|
||||
@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='shellinabox/usercss.c' object='usercss.o' libtool=no @AMDEPBACKSLASH@
|
||||
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
|
||||
@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o usercss.o `test -f 'shellinabox/usercss.c' || echo '$(srcdir)/'`shellinabox/usercss.c
|
||||
|
||||
usercss.obj: shellinabox/usercss.c
|
||||
@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT usercss.obj -MD -MP -MF $(DEPDIR)/usercss.Tpo -c -o usercss.obj `if test -f 'shellinabox/usercss.c'; then $(CYGPATH_W) 'shellinabox/usercss.c'; else $(CYGPATH_W) '$(srcdir)/shellinabox/usercss.c'; fi`
|
||||
@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/usercss.Tpo $(DEPDIR)/usercss.Po
|
||||
@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='shellinabox/usercss.c' object='usercss.obj' libtool=no @AMDEPBACKSLASH@
|
||||
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
|
||||
@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o usercss.obj `if test -f 'shellinabox/usercss.c'; then $(CYGPATH_W) 'shellinabox/usercss.c'; else $(CYGPATH_W) '$(srcdir)/shellinabox/usercss.c'; fi`
|
||||
|
||||
mostlyclean-libtool:
|
||||
-rm -f *.lo
|
||||
|
||||
|
|
2
config.h
2
config.h
|
@ -138,7 +138,7 @@
|
|||
#define STDC_HEADERS 1
|
||||
|
||||
/* Most recent revision number in the version control system */
|
||||
#define VCS_REVISION "164"
|
||||
#define VCS_REVISION "165"
|
||||
|
||||
/* Version number of package */
|
||||
#define VERSION "2.9"
|
||||
|
|
2
configure
vendored
2
configure
vendored
|
@ -2317,7 +2317,7 @@ ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $
|
|||
ac_compiler_gnu=$ac_cv_c_compiler_gnu
|
||||
|
||||
|
||||
VCS_REVISION=164
|
||||
VCS_REVISION=165
|
||||
|
||||
|
||||
cat >>confdefs.h <<_ACEOF
|
||||
|
|
|
@ -2,7 +2,7 @@ AC_PREREQ(2.57)
|
|||
|
||||
dnl This is the one location where the authoritative version number is stored
|
||||
AC_INIT(shellinabox, 2.9, markus@shellinabox.com)
|
||||
VCS_REVISION=164
|
||||
VCS_REVISION=165
|
||||
AC_SUBST(VCS_REVISION)
|
||||
AC_DEFINE_UNQUOTED(VCS_REVISION, "${VCS_REVISION}",
|
||||
[Most recent revision number in the version control system])
|
||||
|
|
127
demo/vt100.js
127
demo/vt100.js
|
@ -259,6 +259,108 @@ VT100.prototype.addListener = function(elem, event, listener) {
|
|||
}
|
||||
};
|
||||
|
||||
VT100.prototype.initializeUserCSSStyles = function() {
|
||||
this.usercssActions = [];
|
||||
if (typeof userCSSList != 'undefined') {
|
||||
var menu = '';
|
||||
var group = '';
|
||||
var wasSingleSel = 1;
|
||||
var beginOfGroup = 0;
|
||||
for (var i = 0; i <= userCSSList.length; ++i) {
|
||||
if (i < userCSSList.length) {
|
||||
var label = userCSSList[i][0];
|
||||
var newGroup = userCSSList[i][1];
|
||||
var enabled = userCSSList[i][2];
|
||||
|
||||
// Add user style sheet to document
|
||||
var style = document.createElement('link');
|
||||
var id = document.createAttribute('id');
|
||||
id.nodeValue = 'usercss-' + i;
|
||||
style.setAttributeNode(id);
|
||||
var rel = document.createAttribute('rel');
|
||||
rel.nodeValue = 'stylesheet';
|
||||
style.setAttributeNode(rel);
|
||||
var href = document.createAttribute('href');
|
||||
href.nodeValue = 'usercss-' + i + '.css';
|
||||
style.setAttributeNode(href);
|
||||
var type = document.createAttribute('type');
|
||||
type.nodeValue = 'text/css';
|
||||
style.setAttributeNode(type);
|
||||
document.getElementsByTagName('head')[0].appendChild(style);
|
||||
style.disabled = !enabled;
|
||||
}
|
||||
|
||||
// Add entry to menu
|
||||
if (newGroup || i == userCSSList.length) {
|
||||
if (beginOfGroup != 0 && (i - beginOfGroup > 1 || !wasSingleSel)) {
|
||||
// The last group had multiple entries that are mutually exclusive;
|
||||
// or the previous to last group did. In either case, we need to
|
||||
// append a "<hr />" before we can add the last group to the menu.
|
||||
menu += '<hr />';
|
||||
}
|
||||
wasSingleSel = i - beginOfGroup < 1;
|
||||
menu += group;
|
||||
group = '';
|
||||
|
||||
for (var j = beginOfGroup; j < i; ++j) {
|
||||
this.usercssActions[this.usercssActions.length] =
|
||||
function(vt100, current, begin, count) {
|
||||
|
||||
// Deselect all other entries in the group, then either select
|
||||
// (for multiple entries in group) or toggle (for on/off entry)
|
||||
// the current entry.
|
||||
return function() {
|
||||
var entry = vt100.getChildById(vt100.menu,
|
||||
'beginusercss');
|
||||
var i = -1;
|
||||
var j = -1;
|
||||
for (var c = count; c > 0; ++j) {
|
||||
if (entry.tagName == 'LI') {
|
||||
if (++i >= begin) {
|
||||
--c;
|
||||
var label = vt100.usercss.childNodes[j];
|
||||
label.innerHTML =
|
||||
label.innerHTML.replace(/^\u2714 /, '');
|
||||
var sheet = document.getElementById(
|
||||
'usercss-' + i);
|
||||
if (i == current) {
|
||||
if (count == 1) {
|
||||
sheet.disabled = !sheet.disabled;
|
||||
} else {
|
||||
sheet.disabled = false;
|
||||
}
|
||||
if (!sheet.disabled) {
|
||||
label.innerHTML= '✔ ' + label.innerHTML;
|
||||
}
|
||||
} else {
|
||||
sheet.disabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
entry = entry.nextSibling;
|
||||
}
|
||||
};
|
||||
}(this, j, beginOfGroup, i - beginOfGroup);
|
||||
}
|
||||
|
||||
if (i == userCSSList.length) {
|
||||
break;
|
||||
}
|
||||
|
||||
beginOfGroup = i;
|
||||
}
|
||||
// Collect all entries in a group, before attaching them to the menu.
|
||||
// This is necessary as we don't know whether this is a group of
|
||||
// mutually exclusive options (which should be separated by "<hr />" on
|
||||
// both ends), or whether this is a on/off toggle, which can be grouped
|
||||
// together with other on/off options.
|
||||
group +=
|
||||
'<li>' + (enabled ? '✔ ' : '') + label + '</li>';
|
||||
}
|
||||
this.usercss.innerHTML = menu;
|
||||
}
|
||||
};
|
||||
|
||||
VT100.prototype.initializeElements = function(container) {
|
||||
// If the necessary objects have not already been defined in the HTML
|
||||
// page, create them now.
|
||||
|
@ -279,6 +381,7 @@ VT100.prototype.initializeElements = function(container) {
|
|||
!this.getChildById(this.container, 'padding') ||
|
||||
!this.getChildById(this.container, 'cursor') ||
|
||||
!this.getChildById(this.container, 'lineheight') ||
|
||||
!this.getChildById(this.container, 'usercss') ||
|
||||
!this.getChildById(this.container, 'space') ||
|
||||
!this.getChildById(this.container, 'input') ||
|
||||
!this.getChildById(this.container, 'cliphelper') ||
|
||||
|
@ -325,6 +428,7 @@ VT100.prototype.initializeElements = function(container) {
|
|||
'<pre id="cursor"> </pre>' +
|
||||
'</div>' +
|
||||
'<div class="hidden">' +
|
||||
'<div id="usercss"></div>' +
|
||||
'<pre><div><span id="space"></span></div></pre>' +
|
||||
'<input type="textfield" id="input" />' +
|
||||
'<input type="textfield" id="cliphelper" />' +
|
||||
|
@ -365,12 +469,16 @@ VT100.prototype.initializeElements = function(container) {
|
|||
var ieProbe = this.getChildById(this.container, 'ieprobe');
|
||||
this.padding = this.getChildById(this.container, 'padding');
|
||||
this.cursor = this.getChildById(this.container, 'cursor');
|
||||
this.usercss = this.getChildById(this.container, 'usercss');
|
||||
this.space = this.getChildById(this.container, 'space');
|
||||
this.input = this.getChildById(this.container, 'input');
|
||||
this.cliphelper = this.getChildById(this.container,
|
||||
'cliphelper');
|
||||
this.attributeHelper = this.getChildById(this.container, 'attrib');
|
||||
|
||||
// Add any user selectable style sheets to the menu
|
||||
this.initializeUserCSSStyles();
|
||||
|
||||
// Remember the dimensions of a standard character glyph. We would
|
||||
// expect that we could just check cursor.clientWidth/Height at any time,
|
||||
// but it turns out that browsers sometimes invalidate these values
|
||||
|
@ -1693,7 +1801,7 @@ VT100.prototype.toggleBell = function() {
|
|||
};
|
||||
|
||||
VT100.prototype.about = function() {
|
||||
alert("VT100 Terminal Emulator " + "2.9 (revision 164)" +
|
||||
alert("VT100 Terminal Emulator " + "2.9 (revision 165)" +
|
||||
"\nCopyright 2008-2009 by Markus Gutschke\n" +
|
||||
"For more information check http://shellinabox.com");
|
||||
};
|
||||
|
@ -1724,7 +1832,11 @@ VT100.prototype.showContextMenu = function(x, y) {
|
|||
(this.utfEnabled ? '✔ ' : '') + 'Unicode</li>' +
|
||||
'<li id="endconfig">' +
|
||||
(this.visualBell ? '✔ ' : '') + 'Visual Bell</li>'+
|
||||
'<hr />' +
|
||||
(this.usercss.firstChild ?
|
||||
'<hr id="beginusercss" />' +
|
||||
this.usercss.innerHTML +
|
||||
'<hr id="endusercss" />' :
|
||||
'<hr />') +
|
||||
'<li id="about">About...</li>' +
|
||||
'</ul>' +
|
||||
'</td></tr>' +
|
||||
|
@ -1744,9 +1856,16 @@ VT100.prototype.showContextMenu = function(x, y) {
|
|||
menuentries.childNodes[1].className
|
||||
= 'disabled';
|
||||
}
|
||||
|
||||
// Actions for default items
|
||||
var actions = [ this.copyLast, p, this.reset,
|
||||
this.toggleUTF, this.toggleBell,
|
||||
this.about ];
|
||||
this.toggleUTF, this.toggleBell ];
|
||||
|
||||
// Actions for user CSS styles (if any)
|
||||
for (var i = 0; i < this.usercssActions.length; ++i) {
|
||||
actions[actions.length] = this.usercssActions[i];
|
||||
}
|
||||
actions[actions.length] = this.about;
|
||||
|
||||
// Allow subclasses to dynamically add entries to the context menu
|
||||
this.extendContextMenu(menuentries, actions);
|
||||
|
|
|
@ -78,21 +78,22 @@
|
|||
--></script>
|
||||
<link rel="stylesheet" href="styles.css" type="text/css">
|
||||
<script type="text/javascript"><!--
|
||||
|
||||
// We would like to hide overflowing lines as this can lead to
|
||||
// visually jarring results if the browser substitutes oversized
|
||||
// Unicode characters from different fonts. Unfortunately, a bug
|
||||
// in Firefox prevents it from allowing multi-line text
|
||||
// selections whenever we change the "overflow" style. So, only
|
||||
// do so for non-Netscape browsers.
|
||||
if (typeof navigator.appName == 'undefined' ||
|
||||
navigator.appName != 'Netscape') {
|
||||
document.write('<style type="text/css">' +
|
||||
'#vt100 #console div, #vt100 #alt_console div {' +
|
||||
' overflow: hidden;' +
|
||||
'}' +
|
||||
'</style>');
|
||||
}
|
||||
(function() {
|
||||
// We would like to hide overflowing lines as this can lead to
|
||||
// visually jarring results if the browser substitutes oversized
|
||||
// Unicode characters from different fonts. Unfortunately, a bug
|
||||
// in Firefox prevents it from allowing multi-line text
|
||||
// selections whenever we change the "overflow" style. So, only
|
||||
// do so for non-Netscape browsers.
|
||||
if (typeof navigator.appName == 'undefined' ||
|
||||
navigator.appName != 'Netscape') {
|
||||
document.write('<style type="text/css">' +
|
||||
'#vt100 #console div, #vt100 #alt_console div {' +
|
||||
' overflow: hidden;' +
|
||||
'}' +
|
||||
'</style>');
|
||||
}
|
||||
})();
|
||||
--></script>
|
||||
<link rel="shortcut icon" href="favicon.ico" type="image/x-icon">
|
||||
<script type="text/javascript" src="ShellInABox.js"></script>
|
||||
|
|
|
@ -50,10 +50,10 @@
|
|||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "shellinabox/service.h"
|
||||
#include "logging/logging.h"
|
||||
#include "shellinabox/launcher.h"
|
||||
#include "shellinabox/privileges.h"
|
||||
#include "logging/logging.h"
|
||||
#include "shellinabox/service.h"
|
||||
|
||||
|
||||
struct Service **services;
|
||||
|
|
|
@ -355,7 +355,7 @@ ShellInABox.prototype.extendContextMenu = function(entries, actions) {
|
|||
};
|
||||
|
||||
ShellInABox.prototype.about = function() {
|
||||
alert("Shell In A Box version " + "2.9 (revision 164)" +
|
||||
alert("Shell In A Box version " + "2.9 (revision 165)" +
|
||||
"\nCopyright 2008-2009 by Markus Gutschke\n" +
|
||||
"For more information check http://shellinabox.com" +
|
||||
(typeof serverSupportsSSL != 'undefined' && serverSupportsSSL ?
|
||||
|
|
|
@ -72,26 +72,28 @@
|
|||
#include "shellinabox/privileges.h"
|
||||
#include "shellinabox/service.h"
|
||||
#include "shellinabox/session.h"
|
||||
#include "shellinabox/usercss.h"
|
||||
|
||||
#define PORTNUM 4200
|
||||
#define MAX_RESPONSE 2048
|
||||
|
||||
static int port;
|
||||
static int portMin;
|
||||
static int portMax;
|
||||
static int localhostOnly = 0;
|
||||
static int noBeep = 0;
|
||||
static int numericHosts = 0;
|
||||
static int enableSSL = 1;
|
||||
static int enableSSLMenu = 1;
|
||||
static int linkifyURLs = 1;
|
||||
static char *certificateDir;
|
||||
static int certificateFd = -1;
|
||||
static HashMap *externalFiles;
|
||||
static Server *cgiServer;
|
||||
static char *cgiSessionKey;
|
||||
static int cgiSessions;
|
||||
static char *cssStyleSheet;
|
||||
static int port;
|
||||
static int portMin;
|
||||
static int portMax;
|
||||
static int localhostOnly = 0;
|
||||
static int noBeep = 0;
|
||||
static int numericHosts = 0;
|
||||
static int enableSSL = 1;
|
||||
static int enableSSLMenu = 1;
|
||||
static int linkifyURLs = 1;
|
||||
static char *certificateDir;
|
||||
static int certificateFd = -1;
|
||||
static HashMap *externalFiles;
|
||||
static Server *cgiServer;
|
||||
static char *cgiSessionKey;
|
||||
static int cgiSessions;
|
||||
static char *cssStyleSheet;
|
||||
static struct UserCSS *userCSSList;
|
||||
|
||||
static char *jsonEscape(const char *buf, int len) {
|
||||
static const char *hexDigit = "0123456789ABCDEF";
|
||||
|
@ -517,15 +519,18 @@ static int shellInABoxHttpHandler(HttpConnection *http, void *arg,
|
|||
extern char vt100End[];
|
||||
extern char shellInABoxStart[];
|
||||
extern char shellInABoxEnd[];
|
||||
char *userCSSString = getUserCSSString(userCSSList);
|
||||
char *stateVars = stringPrintf(NULL,
|
||||
"serverSupportsSSL = %s;\n"
|
||||
"disableSSLMenu = %s;\n"
|
||||
"suppressAllAudio = %s;\n"
|
||||
"linkifyURLs = %d;\n\n",
|
||||
"linkifyURLs = %d;\n"
|
||||
"userCSSList = %s;\n\n",
|
||||
enableSSL ? "true" : "false",
|
||||
!enableSSLMenu ? "true" : "false",
|
||||
noBeep ? "true" : "false",
|
||||
linkifyURLs);
|
||||
linkifyURLs, userCSSString);
|
||||
free(userCSSString);
|
||||
int stateVarsLength = strlen(stateVars);
|
||||
int contentLength = stateVarsLength +
|
||||
(addr(vt100End) - addr(vt100Start)) +
|
||||
|
@ -552,6 +557,18 @@ static int shellInABoxHttpHandler(HttpConnection *http, void *arg,
|
|||
// Serve the style sheet.
|
||||
serveStaticFile(http, "text/css; charset=utf-8",
|
||||
cssStyleSheet, strrchr(cssStyleSheet, '\000'));
|
||||
} else if (pathInfoLength > 8 && !memcmp(pathInfo, "usercss-", 8)) {
|
||||
// Server user style sheets (if any)
|
||||
struct UserCSS *css = userCSSList;
|
||||
for (int idx = atoi(pathInfo + 8);
|
||||
idx-- > 0 && css; css = css->next ) {
|
||||
}
|
||||
if (css) {
|
||||
serveStaticFile(http, "text/css; charset=utf-8",
|
||||
css->style, css->style + css->styleLen);
|
||||
} else {
|
||||
httpSendReply(http, 404, "File not found", NO_MSG);
|
||||
}
|
||||
} else {
|
||||
httpSendReply(http, 404, "File not found", NO_MSG);
|
||||
}
|
||||
|
@ -605,13 +622,14 @@ static void usage(void) {
|
|||
"%s"
|
||||
" -q, --quiet turn off all messages\n"
|
||||
" -u, --user=UID switch to this user (default: %s)\n"
|
||||
" --user-css=STYLES defines user-selectable CSS options\n"
|
||||
" -v, --verbose enable logging messages\n"
|
||||
" --version prints version information\n"
|
||||
"\n"
|
||||
"Debug, quiet, and verbose are mutually exclusive.\n"
|
||||
"\n"
|
||||
"One or more --service arguments define services that should "
|
||||
"be made available \n"
|
||||
"be made available\n"
|
||||
"through the web interface:\n"
|
||||
" SERVICE := <url-path> ':' APP\n"
|
||||
" APP := 'LOGIN' | 'SSH' [ : <host> ] | "
|
||||
|
@ -627,7 +645,18 @@ static void usage(void) {
|
|||
" ${lines} - number of rows\n"
|
||||
" ${peer} - name of remote peer\n"
|
||||
" ${uid} - user id\n"
|
||||
" ${user} - user name",
|
||||
" ${user} - user name\n"
|
||||
"\n"
|
||||
"One or more --user-css arguments define optional user-selectable "
|
||||
"CSS options.\n"
|
||||
"These options show up in the right-click context menu:\n"
|
||||
" STYLES := GROUP { ';' GROUP }*\n"
|
||||
" GROUP := OPTION { ',' OPTION }*\n"
|
||||
" OPTION := <label> ':' [ '-' | '+' ] <css-file>\n"
|
||||
"\n"
|
||||
"OPTIONs that make up a GROUP are mutually exclusive. But "
|
||||
"individual GROUPs are\n"
|
||||
"independent of each other.\n",
|
||||
!serverSupportsSSL() ? "" :
|
||||
" -c, --cert=CERTDIR set certificate dir "
|
||||
"(default: $PWD)\n"
|
||||
|
@ -685,6 +714,7 @@ static void parseArgs(int argc, char * const argv[]) {
|
|||
{ "disable-ssl-menu", 0, 0, 0 },
|
||||
{ "quiet", 0, 0, 'q' },
|
||||
{ "user", 1, 0, 'u' },
|
||||
{ "user-css", 1, 0, 0 },
|
||||
{ "verbose", 0, 0, 'v' },
|
||||
{ "version", 0, 0, 0 },
|
||||
{ 0, 0, 0, 0 } };
|
||||
|
@ -703,7 +733,7 @@ static void parseArgs(int argc, char * const argv[]) {
|
|||
if (idx-- <= 0) {
|
||||
// Help (or invalid argument)
|
||||
usage();
|
||||
if (idx == -1) {
|
||||
if (idx < -1) {
|
||||
fatal("Failed to parse command line");
|
||||
}
|
||||
exit(0);
|
||||
|
@ -894,6 +924,12 @@ static void parseArgs(int argc, char * const argv[]) {
|
|||
fatal("\"--user\" expects a user name.");
|
||||
}
|
||||
runAsUser = parseUser(optarg, NULL);
|
||||
} else if (!idx--) {
|
||||
// User CSS
|
||||
if (!optarg || !*optarg) {
|
||||
fatal("\"--user-css\" expects a list of styles sheets and labels");
|
||||
}
|
||||
parseUserCSS(&userCSSList, optarg);
|
||||
} else if (!idx--) {
|
||||
// Verbose
|
||||
if (!logIsDefault() && (!logIsInfo() || logIsDebug())) {
|
||||
|
|
210
shellinabox/usercss.c
Normal file
210
shellinabox/usercss.c
Normal file
|
@ -0,0 +1,210 @@
|
|||
// usercss.c -- Defines user-selectable CSS options
|
||||
// Copyright (C) 2008-2009 Markus Gutschke <markus@shellinabox.com>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
//
|
||||
// In addition to these license terms, the author grants the following
|
||||
// additional rights:
|
||||
//
|
||||
// If you modify this program, or any covered work, by linking or
|
||||
// combining it with the OpenSSL project's OpenSSL library (or a
|
||||
// modified version of that library), containing parts covered by the
|
||||
// terms of the OpenSSL or SSLeay licenses, the author
|
||||
// grants you additional permission to convey the resulting work.
|
||||
// Corresponding Source for a non-source form of such a combination
|
||||
// shall include the source code for the parts of OpenSSL used as well
|
||||
// as that of the covered work.
|
||||
//
|
||||
// You may at your option choose to remove this additional permission from
|
||||
// the work, or from any part of it.
|
||||
//
|
||||
// It is possible to build this program in a way that it loads OpenSSL
|
||||
// libraries at run-time. If doing so, the following notices are required
|
||||
// by the OpenSSL and SSLeay licenses:
|
||||
//
|
||||
// This product includes software developed by the OpenSSL Project
|
||||
// for use in the OpenSSL Toolkit. (http://www.openssl.org/)
|
||||
//
|
||||
// This product includes cryptographic software written by Eric Young
|
||||
// (eay@cryptsoft.com)
|
||||
//
|
||||
//
|
||||
// The most up-to-date version of this program is always available from
|
||||
// http://shellinabox.com
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "logging/logging.h"
|
||||
#include "shellinabox/usercss.h"
|
||||
|
||||
|
||||
static void readStylesheet(const char *filename, char **style, size_t *len) {
|
||||
int fd = open(filename, O_RDONLY);
|
||||
struct stat st;
|
||||
if (fd < 0 || fstat(fd, &st)) {
|
||||
fatal("Cannot access style sheet \"%s\"", filename);
|
||||
}
|
||||
FILE *fp;
|
||||
check(fp = fdopen(fd, "r"));
|
||||
check(*style = malloc(st.st_size + 1));
|
||||
check(fread(*style, 1, st.st_size, fp) == st.st_size);
|
||||
(*style)[st.st_size] = '\000';
|
||||
*len = st.st_size;
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
void initUserCSS(struct UserCSS *userCSS, const char *arg) {
|
||||
userCSS->newGroup = 1;
|
||||
|
||||
int numMembers = 1;
|
||||
int hasActiveMember = 0;
|
||||
for (;;) {
|
||||
const char *colon = strchr(arg, ':');
|
||||
if (!colon) {
|
||||
fatal("Incomplete user CSS definition: \"%s\"", arg);
|
||||
}
|
||||
|
||||
check(userCSS->label = malloc(6*(colon - arg) + 1));
|
||||
for (const char *src = arg, *dst = userCSS->label;;) {
|
||||
if (src == colon) {
|
||||
*(char *)dst = '\000';
|
||||
break;
|
||||
}
|
||||
char ch = *src++;
|
||||
if (ch == '<') {
|
||||
memcpy((char *)dst, "<", 4);
|
||||
dst += 4;
|
||||
} else if (ch == '&') {
|
||||
memcpy((char *)dst, "&", 5);
|
||||
dst += 5;
|
||||
} else if (ch == '\'') {
|
||||
memcpy((char *)dst, "'", 6);
|
||||
dst += 6;
|
||||
} else if (ch == '"') {
|
||||
memcpy((char *)dst, """, 6);
|
||||
dst += 6;
|
||||
} else {
|
||||
*(char *)dst++ = ch;
|
||||
}
|
||||
}
|
||||
|
||||
int filenameLen = strcspn(colon + 1, ",;");
|
||||
char *filename;
|
||||
check(filename = malloc(filenameLen + 1));
|
||||
memcpy(filename, colon + 1, filenameLen);
|
||||
filename[filenameLen] = '\000';
|
||||
|
||||
switch (*filename) {
|
||||
case '-':
|
||||
userCSS->isActivated = 0;
|
||||
break;
|
||||
case '+':
|
||||
if (hasActiveMember) {
|
||||
fatal("There can only be one active style option per group. Maybe "
|
||||
"use ';' instead of ',' to start a new group.");
|
||||
}
|
||||
hasActiveMember = 1;
|
||||
userCSS->isActivated = 1;
|
||||
break;
|
||||
default:
|
||||
fatal("Must indicate with '+' or '-' whether the style option is "
|
||||
"active by default");
|
||||
}
|
||||
|
||||
readStylesheet(filename + 1, (char **)&userCSS->style, &userCSS->styleLen);
|
||||
free(filename);
|
||||
|
||||
arg = colon + 1 + filenameLen;
|
||||
if (!*arg) {
|
||||
userCSS->next = NULL;
|
||||
break;
|
||||
}
|
||||
check(userCSS->next = malloc(sizeof(struct UserCSS)));
|
||||
userCSS = userCSS->next;
|
||||
userCSS->newGroup = *arg++ == ';';
|
||||
if (userCSS->newGroup) {
|
||||
if (!hasActiveMember && numMembers > 1) {
|
||||
// Print error message
|
||||
break;
|
||||
}
|
||||
numMembers = 1;
|
||||
hasActiveMember = 0;
|
||||
} else {
|
||||
++numMembers;
|
||||
}
|
||||
}
|
||||
if (!hasActiveMember && numMembers > 1) {
|
||||
fatal("Each group of style options must have exactly one style that is "
|
||||
"active by\n"
|
||||
"default.");
|
||||
}
|
||||
}
|
||||
|
||||
struct UserCSS *newUserCSS(const char *arg) {
|
||||
struct UserCSS *userCSS;
|
||||
check(userCSS = malloc(sizeof(struct UserCSS)));
|
||||
initUserCSS(userCSS, arg);
|
||||
return userCSS;
|
||||
}
|
||||
|
||||
void parseUserCSS(struct UserCSS **userCSSList, const char *arg) {
|
||||
while (*userCSSList) {
|
||||
userCSSList = &(*userCSSList)->next;
|
||||
}
|
||||
*userCSSList = newUserCSS(arg);
|
||||
}
|
||||
|
||||
void destroyUserCSS(struct UserCSS *userCSS) {
|
||||
if (userCSS) {
|
||||
free((void *)userCSS->label);
|
||||
userCSS->label = NULL;
|
||||
free((void *)userCSS->style);
|
||||
userCSS->style = NULL;
|
||||
userCSS->styleLen = -1;
|
||||
for (struct UserCSS *child = userCSS->next; child; ) {
|
||||
struct UserCSS *next = child->next;
|
||||
free((void *)child->label);
|
||||
free((void *)child->style);
|
||||
free(child);
|
||||
child = next;
|
||||
}
|
||||
userCSS->next = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void deleteUserCSS(struct UserCSS *userCSS) {
|
||||
destroyUserCSS(userCSS);
|
||||
free(userCSS);
|
||||
}
|
||||
|
||||
char *getUserCSSString(struct UserCSS *userCSS) {
|
||||
char *s = stringPrintf(NULL, "[ ");
|
||||
while (userCSS) {
|
||||
s = stringPrintf(s, "[ '%s', %s, %s ]%s",
|
||||
userCSS->label,
|
||||
userCSS->newGroup ? "true" : "false",
|
||||
userCSS->isActivated ? "true" : "false",
|
||||
userCSS->next ? ", " : "");
|
||||
userCSS = userCSS->next;
|
||||
}
|
||||
return stringPrintf(s, " ]");
|
||||
}
|
65
shellinabox/usercss.h
Normal file
65
shellinabox/usercss.h
Normal file
|
@ -0,0 +1,65 @@
|
|||
// usercss.h -- Defines user-selectable CSS options
|
||||
// Copyright (C) 2008-2009 Markus Gutschke <markus@shellinabox.com>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
//
|
||||
// In addition to these license terms, the author grants the following
|
||||
// additional rights:
|
||||
//
|
||||
// If you modify this program, or any covered work, by linking or
|
||||
// combining it with the OpenSSL project's OpenSSL library (or a
|
||||
// modified version of that library), containing parts covered by the
|
||||
// terms of the OpenSSL or SSLeay licenses, the author
|
||||
// grants you additional permission to convey the resulting work.
|
||||
// Corresponding Source for a non-source form of such a combination
|
||||
// shall include the source code for the parts of OpenSSL used as well
|
||||
// as that of the covered work.
|
||||
//
|
||||
// You may at your option choose to remove this additional permission from
|
||||
// the work, or from any part of it.
|
||||
//
|
||||
// It is possible to build this program in a way that it loads OpenSSL
|
||||
// libraries at run-time. If doing so, the following notices are required
|
||||
// by the OpenSSL and SSLeay licenses:
|
||||
//
|
||||
// This product includes software developed by the OpenSSL Project
|
||||
// for use in the OpenSSL Toolkit. (http://www.openssl.org/)
|
||||
//
|
||||
// This product includes cryptographic software written by Eric Young
|
||||
// (eay@cryptsoft.com)
|
||||
//
|
||||
//
|
||||
// The most up-to-date version of this program is always available from
|
||||
// http://shellinabox.com
|
||||
|
||||
#ifndef USERCSS_H__
|
||||
#define USERCSS_H__
|
||||
|
||||
struct UserCSS {
|
||||
struct UserCSS *next;
|
||||
int newGroup;
|
||||
int isActivated;
|
||||
const char *label;
|
||||
const char *style;
|
||||
size_t styleLen;
|
||||
};
|
||||
|
||||
void initUserCSS(struct UserCSS *userCSS, const char *arg);
|
||||
struct UserCSS *newUserCSS(const char *arg);
|
||||
void parseUserCSS(struct UserCSS **userCSSList, const char *arg);
|
||||
void destroyUserCSS(struct UserCSS *userCSS);
|
||||
void deleteUserCSS(struct UserCSS *userCSS);
|
||||
char *getUserCSSString(struct UserCSS *userCSS);
|
||||
|
||||
#endif
|
|
@ -259,6 +259,108 @@ VT100.prototype.addListener = function(elem, event, listener) {
|
|||
}
|
||||
};
|
||||
|
||||
VT100.prototype.initializeUserCSSStyles = function() {
|
||||
this.usercssActions = [];
|
||||
if (typeof userCSSList != 'undefined') {
|
||||
var menu = '';
|
||||
var group = '';
|
||||
var wasSingleSel = 1;
|
||||
var beginOfGroup = 0;
|
||||
for (var i = 0; i <= userCSSList.length; ++i) {
|
||||
if (i < userCSSList.length) {
|
||||
var label = userCSSList[i][0];
|
||||
var newGroup = userCSSList[i][1];
|
||||
var enabled = userCSSList[i][2];
|
||||
|
||||
// Add user style sheet to document
|
||||
var style = document.createElement('link');
|
||||
var id = document.createAttribute('id');
|
||||
id.nodeValue = 'usercss-' + i;
|
||||
style.setAttributeNode(id);
|
||||
var rel = document.createAttribute('rel');
|
||||
rel.nodeValue = 'stylesheet';
|
||||
style.setAttributeNode(rel);
|
||||
var href = document.createAttribute('href');
|
||||
href.nodeValue = 'usercss-' + i + '.css';
|
||||
style.setAttributeNode(href);
|
||||
var type = document.createAttribute('type');
|
||||
type.nodeValue = 'text/css';
|
||||
style.setAttributeNode(type);
|
||||
document.getElementsByTagName('head')[0].appendChild(style);
|
||||
style.disabled = !enabled;
|
||||
}
|
||||
|
||||
// Add entry to menu
|
||||
if (newGroup || i == userCSSList.length) {
|
||||
if (beginOfGroup != 0 && (i - beginOfGroup > 1 || !wasSingleSel)) {
|
||||
// The last group had multiple entries that are mutually exclusive;
|
||||
// or the previous to last group did. In either case, we need to
|
||||
// append a "<hr />" before we can add the last group to the menu.
|
||||
menu += '<hr />';
|
||||
}
|
||||
wasSingleSel = i - beginOfGroup < 1;
|
||||
menu += group;
|
||||
group = '';
|
||||
|
||||
for (var j = beginOfGroup; j < i; ++j) {
|
||||
this.usercssActions[this.usercssActions.length] =
|
||||
function(vt100, current, begin, count) {
|
||||
|
||||
// Deselect all other entries in the group, then either select
|
||||
// (for multiple entries in group) or toggle (for on/off entry)
|
||||
// the current entry.
|
||||
return function() {
|
||||
var entry = vt100.getChildById(vt100.menu,
|
||||
'beginusercss');
|
||||
var i = -1;
|
||||
var j = -1;
|
||||
for (var c = count; c > 0; ++j) {
|
||||
if (entry.tagName == 'LI') {
|
||||
if (++i >= begin) {
|
||||
--c;
|
||||
var label = vt100.usercss.childNodes[j];
|
||||
label.innerHTML =
|
||||
label.innerHTML.replace(/^\u2714 /, '');
|
||||
var sheet = document.getElementById(
|
||||
'usercss-' + i);
|
||||
if (i == current) {
|
||||
if (count == 1) {
|
||||
sheet.disabled = !sheet.disabled;
|
||||
} else {
|
||||
sheet.disabled = false;
|
||||
}
|
||||
if (!sheet.disabled) {
|
||||
label.innerHTML= '✔ ' + label.innerHTML;
|
||||
}
|
||||
} else {
|
||||
sheet.disabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
entry = entry.nextSibling;
|
||||
}
|
||||
};
|
||||
}(this, j, beginOfGroup, i - beginOfGroup);
|
||||
}
|
||||
|
||||
if (i == userCSSList.length) {
|
||||
break;
|
||||
}
|
||||
|
||||
beginOfGroup = i;
|
||||
}
|
||||
// Collect all entries in a group, before attaching them to the menu.
|
||||
// This is necessary as we don't know whether this is a group of
|
||||
// mutually exclusive options (which should be separated by "<hr />" on
|
||||
// both ends), or whether this is a on/off toggle, which can be grouped
|
||||
// together with other on/off options.
|
||||
group +=
|
||||
'<li>' + (enabled ? '✔ ' : '') + label + '</li>';
|
||||
}
|
||||
this.usercss.innerHTML = menu;
|
||||
}
|
||||
};
|
||||
|
||||
VT100.prototype.initializeElements = function(container) {
|
||||
// If the necessary objects have not already been defined in the HTML
|
||||
// page, create them now.
|
||||
|
@ -279,6 +381,7 @@ VT100.prototype.initializeElements = function(container) {
|
|||
!this.getChildById(this.container, 'padding') ||
|
||||
!this.getChildById(this.container, 'cursor') ||
|
||||
!this.getChildById(this.container, 'lineheight') ||
|
||||
!this.getChildById(this.container, 'usercss') ||
|
||||
!this.getChildById(this.container, 'space') ||
|
||||
!this.getChildById(this.container, 'input') ||
|
||||
!this.getChildById(this.container, 'cliphelper') ||
|
||||
|
@ -325,6 +428,7 @@ VT100.prototype.initializeElements = function(container) {
|
|||
'<pre id="cursor"> </pre>' +
|
||||
'</div>' +
|
||||
'<div class="hidden">' +
|
||||
'<div id="usercss"></div>' +
|
||||
'<pre><div><span id="space"></span></div></pre>' +
|
||||
'<input type="textfield" id="input" />' +
|
||||
'<input type="textfield" id="cliphelper" />' +
|
||||
|
@ -365,12 +469,16 @@ VT100.prototype.initializeElements = function(container) {
|
|||
var ieProbe = this.getChildById(this.container, 'ieprobe');
|
||||
this.padding = this.getChildById(this.container, 'padding');
|
||||
this.cursor = this.getChildById(this.container, 'cursor');
|
||||
this.usercss = this.getChildById(this.container, 'usercss');
|
||||
this.space = this.getChildById(this.container, 'space');
|
||||
this.input = this.getChildById(this.container, 'input');
|
||||
this.cliphelper = this.getChildById(this.container,
|
||||
'cliphelper');
|
||||
this.attributeHelper = this.getChildById(this.container, 'attrib');
|
||||
|
||||
// Add any user selectable style sheets to the menu
|
||||
this.initializeUserCSSStyles();
|
||||
|
||||
// Remember the dimensions of a standard character glyph. We would
|
||||
// expect that we could just check cursor.clientWidth/Height at any time,
|
||||
// but it turns out that browsers sometimes invalidate these values
|
||||
|
@ -1693,7 +1801,7 @@ VT100.prototype.toggleBell = function() {
|
|||
};
|
||||
|
||||
VT100.prototype.about = function() {
|
||||
alert("VT100 Terminal Emulator " + "2.9 (revision 164)" +
|
||||
alert("VT100 Terminal Emulator " + "2.9 (revision 165)" +
|
||||
"\nCopyright 2008-2009 by Markus Gutschke\n" +
|
||||
"For more information check http://shellinabox.com");
|
||||
};
|
||||
|
@ -1724,7 +1832,11 @@ VT100.prototype.showContextMenu = function(x, y) {
|
|||
(this.utfEnabled ? '✔ ' : '') + 'Unicode</li>' +
|
||||
'<li id="endconfig">' +
|
||||
(this.visualBell ? '✔ ' : '') + 'Visual Bell</li>'+
|
||||
'<hr />' +
|
||||
(this.usercss.firstChild ?
|
||||
'<hr id="beginusercss" />' +
|
||||
this.usercss.innerHTML +
|
||||
'<hr id="endusercss" />' :
|
||||
'<hr />') +
|
||||
'<li id="about">About...</li>' +
|
||||
'</ul>' +
|
||||
'</td></tr>' +
|
||||
|
@ -1744,9 +1856,16 @@ VT100.prototype.showContextMenu = function(x, y) {
|
|||
menuentries.childNodes[1].className
|
||||
= 'disabled';
|
||||
}
|
||||
|
||||
// Actions for default items
|
||||
var actions = [ this.copyLast, p, this.reset,
|
||||
this.toggleUTF, this.toggleBell,
|
||||
this.about ];
|
||||
this.toggleUTF, this.toggleBell ];
|
||||
|
||||
// Actions for user CSS styles (if any)
|
||||
for (var i = 0; i < this.usercssActions.length; ++i) {
|
||||
actions[actions.length] = this.usercssActions[i];
|
||||
}
|
||||
actions[actions.length] = this.about;
|
||||
|
||||
// Allow subclasses to dynamically add entries to the context menu
|
||||
this.extendContextMenu(menuentries, actions);
|
||||
|
|
|
@ -259,6 +259,108 @@ VT100.prototype.addListener = function(elem, event, listener) {
|
|||
}
|
||||
};
|
||||
|
||||
VT100.prototype.initializeUserCSSStyles = function() {
|
||||
this.usercssActions = [];
|
||||
if (typeof userCSSList != 'undefined') {
|
||||
var menu = '';
|
||||
var group = '';
|
||||
var wasSingleSel = 1;
|
||||
var beginOfGroup = 0;
|
||||
for (var i = 0; i <= userCSSList.length; ++i) {
|
||||
if (i < userCSSList.length) {
|
||||
var label = userCSSList[i][0];
|
||||
var newGroup = userCSSList[i][1];
|
||||
var enabled = userCSSList[i][2];
|
||||
|
||||
// Add user style sheet to document
|
||||
var style = document.createElement('link');
|
||||
var id = document.createAttribute('id');
|
||||
id.nodeValue = 'usercss-' + i;
|
||||
style.setAttributeNode(id);
|
||||
var rel = document.createAttribute('rel');
|
||||
rel.nodeValue = 'stylesheet';
|
||||
style.setAttributeNode(rel);
|
||||
var href = document.createAttribute('href');
|
||||
href.nodeValue = 'usercss-' + i + '.css';
|
||||
style.setAttributeNode(href);
|
||||
var type = document.createAttribute('type');
|
||||
type.nodeValue = 'text/css';
|
||||
style.setAttributeNode(type);
|
||||
document.getElementsByTagName('head')[0].appendChild(style);
|
||||
style.disabled = !enabled;
|
||||
}
|
||||
|
||||
// Add entry to menu
|
||||
if (newGroup || i == userCSSList.length) {
|
||||
if (beginOfGroup != 0 && (i - beginOfGroup > 1 || !wasSingleSel)) {
|
||||
// The last group had multiple entries that are mutually exclusive;
|
||||
// or the previous to last group did. In either case, we need to
|
||||
// append a "<hr />" before we can add the last group to the menu.
|
||||
menu += '<hr />';
|
||||
}
|
||||
wasSingleSel = i - beginOfGroup < 1;
|
||||
menu += group;
|
||||
group = '';
|
||||
|
||||
for (var j = beginOfGroup; j < i; ++j) {
|
||||
this.usercssActions[this.usercssActions.length] =
|
||||
function(vt100, current, begin, count) {
|
||||
|
||||
// Deselect all other entries in the group, then either select
|
||||
// (for multiple entries in group) or toggle (for on/off entry)
|
||||
// the current entry.
|
||||
return function() {
|
||||
var entry = vt100.getChildById(vt100.menu,
|
||||
'beginusercss');
|
||||
var i = -1;
|
||||
var j = -1;
|
||||
for (var c = count; c > 0; ++j) {
|
||||
if (entry.tagName == 'LI') {
|
||||
if (++i >= begin) {
|
||||
--c;
|
||||
var label = vt100.usercss.childNodes[j];
|
||||
label.innerHTML =
|
||||
label.innerHTML.replace(/^\u2714 /, '');
|
||||
var sheet = document.getElementById(
|
||||
'usercss-' + i);
|
||||
if (i == current) {
|
||||
if (count == 1) {
|
||||
sheet.disabled = !sheet.disabled;
|
||||
} else {
|
||||
sheet.disabled = false;
|
||||
}
|
||||
if (!sheet.disabled) {
|
||||
label.innerHTML= '✔ ' + label.innerHTML;
|
||||
}
|
||||
} else {
|
||||
sheet.disabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
entry = entry.nextSibling;
|
||||
}
|
||||
};
|
||||
}(this, j, beginOfGroup, i - beginOfGroup);
|
||||
}
|
||||
|
||||
if (i == userCSSList.length) {
|
||||
break;
|
||||
}
|
||||
|
||||
beginOfGroup = i;
|
||||
}
|
||||
// Collect all entries in a group, before attaching them to the menu.
|
||||
// This is necessary as we don't know whether this is a group of
|
||||
// mutually exclusive options (which should be separated by "<hr />" on
|
||||
// both ends), or whether this is a on/off toggle, which can be grouped
|
||||
// together with other on/off options.
|
||||
group +=
|
||||
'<li>' + (enabled ? '✔ ' : '') + label + '</li>';
|
||||
}
|
||||
this.usercss.innerHTML = menu;
|
||||
}
|
||||
};
|
||||
|
||||
VT100.prototype.initializeElements = function(container) {
|
||||
// If the necessary objects have not already been defined in the HTML
|
||||
// page, create them now.
|
||||
|
@ -279,6 +381,7 @@ VT100.prototype.initializeElements = function(container) {
|
|||
!this.getChildById(this.container, 'padding') ||
|
||||
!this.getChildById(this.container, 'cursor') ||
|
||||
!this.getChildById(this.container, 'lineheight') ||
|
||||
!this.getChildById(this.container, 'usercss') ||
|
||||
!this.getChildById(this.container, 'space') ||
|
||||
!this.getChildById(this.container, 'input') ||
|
||||
!this.getChildById(this.container, 'cliphelper') ||
|
||||
|
@ -325,6 +428,7 @@ VT100.prototype.initializeElements = function(container) {
|
|||
'<pre id="cursor"> </pre>' +
|
||||
'</div>' +
|
||||
'<div class="hidden">' +
|
||||
'<div id="usercss"></div>' +
|
||||
'<pre><div><span id="space"></span></div></pre>' +
|
||||
'<input type="textfield" id="input" />' +
|
||||
'<input type="textfield" id="cliphelper" />' +
|
||||
|
@ -365,12 +469,16 @@ VT100.prototype.initializeElements = function(container) {
|
|||
var ieProbe = this.getChildById(this.container, 'ieprobe');
|
||||
this.padding = this.getChildById(this.container, 'padding');
|
||||
this.cursor = this.getChildById(this.container, 'cursor');
|
||||
this.usercss = this.getChildById(this.container, 'usercss');
|
||||
this.space = this.getChildById(this.container, 'space');
|
||||
this.input = this.getChildById(this.container, 'input');
|
||||
this.cliphelper = this.getChildById(this.container,
|
||||
'cliphelper');
|
||||
this.attributeHelper = this.getChildById(this.container, 'attrib');
|
||||
|
||||
// Add any user selectable style sheets to the menu
|
||||
this.initializeUserCSSStyles();
|
||||
|
||||
// Remember the dimensions of a standard character glyph. We would
|
||||
// expect that we could just check cursor.clientWidth/Height at any time,
|
||||
// but it turns out that browsers sometimes invalidate these values
|
||||
|
@ -1724,7 +1832,11 @@ VT100.prototype.showContextMenu = function(x, y) {
|
|||
(this.utfEnabled ? '✔ ' : '') + 'Unicode</li>' +
|
||||
'<li id="endconfig">' +
|
||||
(this.visualBell ? '✔ ' : '') + 'Visual Bell</li>'+
|
||||
'<hr />' +
|
||||
(this.usercss.firstChild ?
|
||||
'<hr id="beginusercss" />' +
|
||||
this.usercss.innerHTML +
|
||||
'<hr id="endusercss" />' :
|
||||
'<hr />') +
|
||||
'<li id="about">About...</li>' +
|
||||
'</ul>' +
|
||||
'</td></tr>' +
|
||||
|
@ -1744,9 +1856,16 @@ VT100.prototype.showContextMenu = function(x, y) {
|
|||
menuentries.childNodes[1].className
|
||||
= 'disabled';
|
||||
}
|
||||
|
||||
// Actions for default items
|
||||
var actions = [ this.copyLast, p, this.reset,
|
||||
this.toggleUTF, this.toggleBell,
|
||||
this.about ];
|
||||
this.toggleUTF, this.toggleBell ];
|
||||
|
||||
// Actions for user CSS styles (if any)
|
||||
for (var i = 0; i < this.usercssActions.length; ++i) {
|
||||
actions[actions.length] = this.usercssActions[i];
|
||||
}
|
||||
actions[actions.length] = this.about;
|
||||
|
||||
// Allow subclasses to dynamically add entries to the context menu
|
||||
this.extendContextMenu(menuentries, actions);
|
||||
|
|
Loading…
Reference in a new issue