diff --git a/Makefile.am b/Makefile.am
index 10182e7..1c78369 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -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 \
diff --git a/Makefile.in b/Makefile.in
index 856ae63..e61f461 100644
--- a/Makefile.in
+++ b/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
diff --git a/config.h b/config.h
index 8f5d734..d2118c9 100644
--- a/config.h
+++ b/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"
diff --git a/configure b/configure
index 399f436..37e5ae4 100755
--- a/configure
+++ b/configure
@@ -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
diff --git a/configure.ac b/configure.ac
index 3cdd440..b214501 100644
--- a/configure.ac
+++ b/configure.ac
@@ -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])
diff --git a/demo/vt100.js b/demo/vt100.js
index 23dd26f..e66f163 100644
--- a/demo/vt100.js
+++ b/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 "
" before we can add the last group to the menu.
+ menu += '';
+ }
+ 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 "" on
+ // both ends), or whether this is a on/off toggle, which can be grouped
+ // together with other on/off options.
+ group +=
+ '
' + (enabled ? '✔ ' : '') + label + '
';
+ }
+ 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) {
'
' +
'' +
'
' +
+ '' +
'
' +
'' +
'' +
@@ -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' +
'
' +
'' +
'' +
@@ -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);
diff --git a/shellinabox/vt100.jspp b/shellinabox/vt100.jspp
index 49f0b94..36d6828 100644
--- a/shellinabox/vt100.jspp
+++ b/shellinabox/vt100.jspp
@@ -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 "" before we can add the last group to the menu.
+ menu += '';
+ }
+ 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 "" on
+ // both ends), or whether this is a on/off toggle, which can be grouped
+ // together with other on/off options.
+ group +=
+ '
' + (enabled ? '✔ ' : '') + label + '
';
+ }
+ 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) {
'
' +
'
' +
'
' +
+ '' +
'
' +
'' +
'' +
@@ -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' +
'