diff --git a/ChangeLog b/ChangeLog index 62a56d3..763b718 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2009-08-11 Markus Gutschke + + * Added support for user selectable style sheets. Included example + style sheets that allow switching to white-on-black or to monochrome + mode from the right click context menu. + + * Fixed the "|" key on Swedish keyboards. + 2009-07-30 Markus Gutschke * Added the --css command line option to make incremental changes diff --git a/Makefile.am b/Makefile.am index 1c78369..e72456d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -16,15 +16,23 @@ dist_doc_DATA = AUTHORS \ NEWS \ README \ TODO \ - shellinabox/white-on-black.css + shellinabox/white-on-black.css \ + shellinabox/black-on-white.css \ + shellinabox/monochrome.css \ + shellinabox/color.css EXTRA_DIST = demo/beep.wav \ demo/favicon.ico \ demo/demo.html \ demo/demo.js \ demo/demo.jspp \ demo/demo.xml \ + demo/enabled.gif \ demo/styles.css \ demo/vt100.js \ + demo/usercss-0.css \ + demo/usercss-1.css \ + demo/usercss-2.css \ + demo/usercss-3.css \ shellinabox/shellinaboxd.man.in \ shellinabox/shell_in_a_box.js \ shellinabox/vt100.js \ @@ -83,6 +91,7 @@ shellinaboxd_SOURCES = shellinabox/shellinaboxd.c \ shellinabox/vt100.jspp \ shellinabox/shell_in_a_box.jspp \ shellinabox/styles.css \ + shellinabox/enabled.gif \ shellinabox/favicon.ico \ shellinabox/beep.wav \ config.h @@ -122,12 +131,20 @@ ${top_srcdir}/demo/demo.js: ${top_srcdir}/demo/beep.wav \ ${top_srcdir}/demo/demo.jspp \ ${top_srcdir}/demo/favicon.ico \ ${top_srcdir}/demo/styles.css \ - ${top_srcdir}/demo/vt100.js + ${top_srcdir}/demo/vt100.js \ + ${top_srcdir}/demo/usercss-0.css \ + ${top_srcdir}/demo/usercss-1.css \ + ${top_srcdir}/demo/usercss-2.css \ + ${top_srcdir}/demo/usercss-3.css ${top_srcdir}/demo/beep.wav: ${top_srcdir}/shellinabox/beep.wav @rm -f "$@" ln "$<" "$@" +${top_srcdir}/demo/enabled.gif: ${top_srcdir}/shellinabox/enabled.gif + @rm -f "$@" + ln "$<" "$@" + ${top_srcdir}/demo/favicon.ico: ${top_srcdir}/shellinabox/favicon.ico @rm -f "$@" ln "$<" "$@" @@ -136,6 +153,22 @@ ${top_srcdir}/demo/styles.css: ${top_srcdir}/shellinabox/styles.css @rm -f "$@" ln "$<" "$@" +${top_srcdir}/demo/usercss-0.css: ${top_srcdir}/shellinabox/white-on-black.css + @rm -f "$@" + ln "$<" "$@" + +${top_srcdir}/demo/usercss-1.css: ${top_srcdir}/shellinabox/black-on-white.css + @rm -f "$@" + ln "$<" "$@" + +${top_srcdir}/demo/usercss-2.css: ${top_srcdir}/shellinabox/monochrome.css + @rm -f "$@" + ln "$<" "$@" + +${top_srcdir}/demo/usercss-3.css: ${top_srcdir}/shellinabox/color.css + @rm -f "$@" + ln "$<" "$@" + ${top_srcdir}/demo/vt100.js: ${top_srcdir}/shellinabox/vt100.js @rm -f "$@" ln "$<" "$@" @@ -178,6 +211,11 @@ clean-local: @objcopy -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)` \ "$<" "$@" +.gif.o: + @echo objcopy "$<" "$@" + @objcopy -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)` \ + "$<" "$@" + .html.o: @echo objcopy "$<" "$@" @objcopy -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)` \ diff --git a/Makefile.in b/Makefile.in index e61f461..d56aa88 100644 --- a/Makefile.in +++ b/Makefile.in @@ -77,8 +77,8 @@ am_shellinaboxd_OBJECTS = shellinaboxd.$(OBJEXT) \ shellinabox/cgi_root.$(OBJEXT) shellinabox/root_page.$(OBJEXT) \ shellinabox/vt100.$(OBJEXT) \ shellinabox/shell_in_a_box.$(OBJEXT) \ - shellinabox/styles.$(OBJEXT) shellinabox/favicon.$(OBJEXT) \ - shellinabox/beep.$(OBJEXT) + shellinabox/styles.$(OBJEXT) shellinabox/enabled.$(OBJEXT) \ + shellinabox/favicon.$(OBJEXT) shellinabox/beep.$(OBJEXT) shellinaboxd_OBJECTS = $(am_shellinaboxd_OBJECTS) shellinaboxd_DEPENDENCIES = liblogging.la libhttp.la shellinaboxd_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ @@ -255,7 +255,10 @@ dist_doc_DATA = AUTHORS \ NEWS \ README \ TODO \ - shellinabox/white-on-black.css + shellinabox/white-on-black.css \ + shellinabox/black-on-white.css \ + shellinabox/monochrome.css \ + shellinabox/color.css EXTRA_DIST = demo/beep.wav \ demo/favicon.ico \ @@ -263,8 +266,13 @@ EXTRA_DIST = demo/beep.wav \ demo/demo.js \ demo/demo.jspp \ demo/demo.xml \ + demo/enabled.gif \ demo/styles.css \ demo/vt100.js \ + demo/usercss-0.css \ + demo/usercss-1.css \ + demo/usercss-2.css \ + demo/usercss-3.css \ shellinabox/shellinaboxd.man.in \ shellinabox/shell_in_a_box.js \ shellinabox/vt100.js \ @@ -327,6 +335,7 @@ shellinaboxd_SOURCES = shellinabox/shellinaboxd.c \ shellinabox/vt100.jspp \ shellinabox/shell_in_a_box.jspp \ shellinabox/styles.css \ + shellinabox/enabled.gif \ shellinabox/favicon.ico \ shellinabox/beep.wav \ config.h @@ -363,7 +372,7 @@ all: config.h $(MAKE) $(AM_MAKEFLAGS) all-am .SUFFIXES: -.SUFFIXES: .c .css .html .ico .js .jspp .lo .o .obj .wav +.SUFFIXES: .c .css .gif .html .ico .js .jspp .lo .o .obj .wav am--refresh: @: $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @@ -471,6 +480,8 @@ shellinabox/shell_in_a_box.$(OBJEXT): shellinabox/$(am__dirstamp) \ shellinabox/$(DEPDIR)/$(am__dirstamp) shellinabox/styles.$(OBJEXT): shellinabox/$(am__dirstamp) \ shellinabox/$(DEPDIR)/$(am__dirstamp) +shellinabox/enabled.$(OBJEXT): shellinabox/$(am__dirstamp) \ + shellinabox/$(DEPDIR)/$(am__dirstamp) shellinabox/favicon.$(OBJEXT): shellinabox/$(am__dirstamp) \ shellinabox/$(DEPDIR)/$(am__dirstamp) shellinabox/beep.$(OBJEXT): shellinabox/$(am__dirstamp) \ @@ -483,6 +494,7 @@ mostlyclean-compile: -rm -f *.$(OBJEXT) -rm -f shellinabox/beep.$(OBJEXT) -rm -f shellinabox/cgi_root.$(OBJEXT) + -rm -f shellinabox/enabled.$(OBJEXT) -rm -f shellinabox/favicon.$(OBJEXT) -rm -f shellinabox/root_page.$(OBJEXT) -rm -f shellinabox/shell_in_a_box.$(OBJEXT) @@ -1052,12 +1064,20 @@ ${top_srcdir}/demo/demo.js: ${top_srcdir}/demo/beep.wav \ ${top_srcdir}/demo/demo.jspp \ ${top_srcdir}/demo/favicon.ico \ ${top_srcdir}/demo/styles.css \ - ${top_srcdir}/demo/vt100.js + ${top_srcdir}/demo/vt100.js \ + ${top_srcdir}/demo/usercss-0.css \ + ${top_srcdir}/demo/usercss-1.css \ + ${top_srcdir}/demo/usercss-2.css \ + ${top_srcdir}/demo/usercss-3.css ${top_srcdir}/demo/beep.wav: ${top_srcdir}/shellinabox/beep.wav @rm -f "$@" ln "$<" "$@" +${top_srcdir}/demo/enabled.gif: ${top_srcdir}/shellinabox/enabled.gif + @rm -f "$@" + ln "$<" "$@" + ${top_srcdir}/demo/favicon.ico: ${top_srcdir}/shellinabox/favicon.ico @rm -f "$@" ln "$<" "$@" @@ -1066,6 +1086,22 @@ ${top_srcdir}/demo/styles.css: ${top_srcdir}/shellinabox/styles.css @rm -f "$@" ln "$<" "$@" +${top_srcdir}/demo/usercss-0.css: ${top_srcdir}/shellinabox/white-on-black.css + @rm -f "$@" + ln "$<" "$@" + +${top_srcdir}/demo/usercss-1.css: ${top_srcdir}/shellinabox/black-on-white.css + @rm -f "$@" + ln "$<" "$@" + +${top_srcdir}/demo/usercss-2.css: ${top_srcdir}/shellinabox/monochrome.css + @rm -f "$@" + ln "$<" "$@" + +${top_srcdir}/demo/usercss-3.css: ${top_srcdir}/shellinabox/color.css + @rm -f "$@" + ln "$<" "$@" + ${top_srcdir}/demo/vt100.js: ${top_srcdir}/shellinabox/vt100.js @rm -f "$@" ln "$<" "$@" @@ -1108,6 +1144,11 @@ clean-local: @objcopy -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)` \ "$<" "$@" +.gif.o: + @echo objcopy "$<" "$@" + @objcopy -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)` \ + "$<" "$@" + .html.o: @echo objcopy "$<" "$@" @objcopy -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)` \ diff --git a/config.h b/config.h index d139dc5..5fc2644 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 "166" +#define VCS_REVISION "167" /* Version number of package */ #define VERSION "2.9" diff --git a/configure b/configure index a9b93d9..2965c7f 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=166 +VCS_REVISION=167 cat >>confdefs.h <<_ACEOF diff --git a/configure.ac b/configure.ac index f640637..e031716 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=166 +VCS_REVISION=167 AC_SUBST(VCS_REVISION) AC_DEFINE_UNQUOTED(VCS_REVISION, "${VCS_REVISION}", [Most recent revision number in the version control system]) diff --git a/debian/docs b/debian/docs index bc017c5..34380ab 100644 --- a/debian/docs +++ b/debian/docs @@ -6,3 +6,6 @@ NEWS README TODO shellinabox/white-on-black.css +shellinabox/black-on-white.css +shellinabox/monochrome.css +shellinabox/color.css diff --git a/demo/demo.html b/demo/demo.html index b726bfe..aa41988 100644 --- a/demo/demo.html +++ b/demo/demo.html @@ -64,6 +64,13 @@ '}' + ''); } + + suppressAllAudio = true; + linkifyURLs = 1; + userCSSList = [ [ 'White on Black', true, false ], + [ 'Black on White', false, true ], + [ 'Monochrome', true, false ], + [ 'Color Terminal', false, true ] ]; --> diff --git a/demo/enabled.gif b/demo/enabled.gif new file mode 100644 index 0000000..07936e2 Binary files /dev/null and b/demo/enabled.gif differ diff --git a/demo/styles.css b/demo/styles.css index ab66973..6e465a1 100644 --- a/demo/styles.css +++ b/demo/styles.css @@ -127,22 +127,47 @@ margin: 0.5ex 0px 0.5ex 0px; } -#vt100 #ansi0 { background-color: #000000; } -#vt100 #ansi1 { background-color: #cd0000; } -#vt100 #ansi2 { background-color: #00cd00; } -#vt100 #ansi3 { background-color: #cdcd00; } -#vt100 #ansi4 { background-color: #0000ee; } -#vt100 #ansi5 { background-color: #cd00cd; } -#vt100 #ansi6 { background-color: #00cdcd; } -#vt100 #ansi7 { background-color: #e5e5e5; } -#vt100 #ansi8 { background-color: #7f7f7f; } -#vt100 #ansi9 { background-color: #ff0000; } -#vt100 #ansi10 { background-color: #00ff00; } -#vt100 #ansi11 { background-color: #e8e800; } -#vt100 #ansi12 { background-color: #5c5cff; } -#vt100 #ansi13 { background-color: #ff00ff; } -#vt100 #ansi14 { background-color: #00ffff; } -#vt100 #ansi15 { background-color: #ffffff; } +#vt100 #menu img { + margin-right: 0.5ex; + width: 1ex; + height: 1ex; +} + +#vt100 #scrollable.inverted { color: #ffffff; + background-color: #000000; } +#vt100 .ansi0 { } +#vt100 .ansi1 { color: #cd0000; } +#vt100 .ansi2 { color: #00cd00; } +#vt100 .ansi3 { color: #cdcd00; } +#vt100 .ansi4 { color: #0000ee; } +#vt100 .ansi5 { color: #cd00cd; } +#vt100 .ansi6 { color: #00cdcd; } +#vt100 .ansi7 { color: #e5e5e5; } +#vt100 .ansi8 { color: #7f7f7f; } +#vt100 .ansi9 { color: #ff0000; } +#vt100 .ansi10 { color: #00ff00; } +#vt100 .ansi11 { color: #e8e800; } +#vt100 .ansi12 { color: #5c5cff; } +#vt100 .ansi13 { color: #ff00ff; } +#vt100 .ansi14 { color: #00ffff; } +#vt100 .ansi15 { color: #ffffff; } + +#vt100 .bgAnsi0 { background-color: #000000; } +#vt100 .bgAnsi1 { background-color: #cd0000; } +#vt100 .bgAnsi2 { background-color: #00cd00; } +#vt100 .bgAnsi3 { background-color: #cdcd00; } +#vt100 .bgAnsi4 { background-color: #0000ee; } +#vt100 .bgAnsi5 { background-color: #cd00cd; } +#vt100 .bgAnsi6 { background-color: #00cdcd; } +#vt100 .bgAnsi7 { background-color: #e5e5e5; } +#vt100 .bgAnsi8 { background-color: #7f7f7f; } +#vt100 .bgAnsi9 { background-color: #ff0000; } +#vt100 .bgAnsi10 { background-color: #00ff00; } +#vt100 .bgAnsi11 { background-color: #e8e800; } +#vt100 .bgAnsi12 { background-color: #5c5cff; } +#vt100 .bgAnsi13 { background-color: #ff00ff; } +#vt100 .bgAnsi14 { background-color: #00ffff; } +#vt100 .bgAnsi15 { } @media print { #vt100 .scrollback { diff --git a/demo/usercss-0.css b/demo/usercss-0.css new file mode 100644 index 0000000..c64d5b1 --- /dev/null +++ b/demo/usercss-0.css @@ -0,0 +1,6 @@ +#vt100 #scrollable { color: #ffffff; + background-color: #000000; } +#vt100 #scrollable.inverted { color: #000000; + background-color: #ffffff; } +#vt100 .ansi15 { color: #000000; } +#vt100 .bgAnsi0 { background-color: #ffffff; } diff --git a/demo/usercss-1.css b/demo/usercss-1.css new file mode 100644 index 0000000..e69de29 diff --git a/demo/usercss-2.css b/demo/usercss-2.css new file mode 100644 index 0000000..b5c60a5 --- /dev/null +++ b/demo/usercss-2.css @@ -0,0 +1,29 @@ +#vt100 .ansi1 { color: inherit; } +#vt100 .ansi2 { color: inherit; } +#vt100 .ansi3 { color: inherit; } +#vt100 .ansi4 { color: inherit; } +#vt100 .ansi5 { color: inherit; } +#vt100 .ansi6 { color: inherit; } +#vt100 .ansi7 { color: inherit; } +#vt100 .ansi8 { color: inherit; } +#vt100 .ansi9 { color: inherit; } +#vt100 .ansi10 { color: inherit; } +#vt100 .ansi11 { color: inherit; } +#vt100 .ansi12 { color: inherit; } +#vt100 .ansi13 { color: inherit; } +#vt100 .ansi14 { color: inherit; } + +#vt100 .bgAnsi1 { background-color: inherit; } +#vt100 .bgAnsi2 { background-color: inherit; } +#vt100 .bgAnsi3 { background-color: inherit; } +#vt100 .bgAnsi4 { background-color: inherit; } +#vt100 .bgAnsi5 { background-color: inherit; } +#vt100 .bgAnsi6 { background-color: inherit; } +#vt100 .bgAnsi7 { background-color: inherit; } +#vt100 .bgAnsi8 { background-color: inherit; } +#vt100 .bgAnsi9 { background-color: inherit; } +#vt100 .bgAnsi10 { background-color: inherit; } +#vt100 .bgAnsi11 { background-color: inherit; } +#vt100 .bgAnsi12 { background-color: inherit; } +#vt100 .bgAnsi13 { background-color: inherit; } +#vt100 .bgAnsi14 { background-color: inherit; } diff --git a/demo/usercss-3.css b/demo/usercss-3.css new file mode 100644 index 0000000..e69de29 diff --git a/demo/vt100.js b/demo/vt100.js index 15a555b..96fcc2b 100644 --- a/demo/vt100.js +++ b/demo/vt100.js @@ -174,7 +174,6 @@ function VT100(container) { '(?:[?](?:(?![ \u00A0]|[,.)}"\u0027!]+[ \u00A0]|[,.)}"\u0027!]+$).)*)?'); } this.initializeElements(container); - this.initializeAnsiColors(); this.maxScrollbackLines = 500; this.npar = 0; this.par = [ ]; @@ -210,6 +209,7 @@ VT100.prototype.reset = function(clearHistory) { suppressAllAudio; this.utfCount = 0; this.utfChar = 0; + this.color = 'ansi0 bgAnsi15'; this.style = ''; this.attr = 0x00F0 /* ATTR_DEFAULT */; this.useGMap = 0; @@ -236,19 +236,8 @@ VT100.prototype.reset = function(clearHistory) { this.showCursor(); this.isInverted = false; this.refreshInvertedState(); - this.clearRegion(0, 0, this.terminalWidth, this.terminalHeight, this.style); -}; - -VT100.prototype.initializeAnsiColors = function() { - var elem = document.createElement('pre'); - this.container.appendChild(elem); - this.setTextContent(elem, ' '); - this.ansi = [ ]; - for (var i = 0; i < 16; i++) { - elem.id = 'ansi' + i; - this.ansi[i] = this.getCurrentComputedStyle(elem, 'backgroundColor'); - } - this.container.removeChild(elem); + this.clearRegion(0, 0, this.terminalWidth, this.terminalHeight, + this.color, this.style); }; VT100.prototype.addListener = function(elem, event, listener) { @@ -319,8 +308,17 @@ VT100.prototype.initializeUserCSSStyles = function() { if (++i >= begin) { --c; var label = vt100.usercss.childNodes[j]; - label.innerHTML = - label.innerHTML.replace(/^\u2714 /, ''); + + // Restore label to just the text content + if (typeof label.textContent == 'undefined') { + var s = label.innerText; + label.innerHTML = ''; + label.appendChild(document.createTextNode(s)); + } else { + label.textContent= label.textContent; + } + + // User style sheets are number sequentially var sheet = document.getElementById( 'usercss-' + i); if (i == current) { @@ -330,7 +328,8 @@ VT100.prototype.initializeUserCSSStyles = function() { sheet.disabled = false; } if (!sheet.disabled) { - label.innerHTML= '✔ ' + label.innerHTML; + label.innerHTML= '' + + label.innerHTML; } } else { sheet.disabled = true; @@ -355,7 +354,9 @@ VT100.prototype.initializeUserCSSStyles = function() { // both ends), or whether this is a on/off toggle, which can be grouped // together with other on/off options. group += - '
  • ' + (enabled ? '✔ ' : '') + label + '
  • '; + '
  • ' + (enabled ? '' : '') + + label + + '
  • '; } this.usercss.innerHTML = menu; } @@ -384,8 +385,7 @@ VT100.prototype.initializeElements = function(container) { !this.getChildById(this.container, 'usercss') || !this.getChildById(this.container, 'space') || !this.getChildById(this.container, 'input') || - !this.getChildById(this.container, 'cliphelper') || - !this.getChildById(this.container, 'attrib')) { + !this.getChildById(this.container, 'cliphelper')) { // Only enable the "embed" object, if we have a suitable plugin. Otherwise, // we might get a pointless warning that a suitable plugin is not yet // installed. If in doubt, we'd rather just stay silent. @@ -432,7 +432,6 @@ VT100.prototype.initializeElements = function(container) { '
    ' + '' + '' + - ' ' + (typeof suppressAllAudio != 'undefined' && suppressAllAudio ? "" : embed + '') + @@ -474,7 +473,6 @@ VT100.prototype.initializeElements = function(container) { 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(); @@ -636,12 +634,13 @@ VT100.prototype.repairElements = function(console) { for (var line = console.firstChild; line; line = line.nextSibling) { if (!line.clientHeight) { var newLine = document.createElement(line.tagName); - newLine.style.cssText = line.style.cssText; - newLine.className = line.className; + newLine.style.cssText = line.style.cssText; + newLine.className = line.className; if (line.tagName == 'DIV') { for (var span = line.firstChild; span; span = span.nextSibling) { - var newSpan = document.createElement(span.tagName); - newSpan.style.cssText = span.style.cssText; + var newSpan = document.createElement(span.tagName); + newSpan.style.cssText = span.style.cssText; + newSpan.style.className = span.style.className; this.setTextContent(newSpan, this.getTextContent(span)); newLine.appendChild(newSpan); } @@ -649,7 +648,7 @@ VT100.prototype.repairElements = function(console) { this.setTextContent(newLine, this.getTextContent(line)); } line.parentNode.replaceChild(newLine, line); - line = newLine; + line = newLine; } } }; @@ -1015,7 +1014,7 @@ VT100.prototype.setTextContent = function(elem, s) { } }; -VT100.prototype.insertBlankLine = function(y, style) { +VT100.prototype.insertBlankLine = function(y, color, style) { // Insert a blank line a position y. This method ignores the scrollback // buffer. The caller has to add the length of the scrollback buffer to // the position, if necessary. @@ -1023,22 +1022,26 @@ VT100.prototype.insertBlankLine = function(y, style) { // method just adds a new line right after the last existing one. It does // not add any missing lines in between. It is the caller's responsibility // to do so. - if (style == undefined) { - style = ''; + if (!color) { + color = 'ansi0 bgAnsi15'; + } + if (!style) { + style = ''; } var line; - if (!style) { - line = document.createElement('pre'); + if (color != 'ansi0 bgAnsi15' && !style) { + line = document.createElement('pre'); this.setTextContent(line, '\n'); } else { - line = document.createElement('div'); - var span = document.createElement('span'); - span.style.cssText = style; + line = document.createElement('div'); + var span = document.createElement('span'); + span.style.cssText = style; + span.style.className = color; this.setTextContent(span, this.spaces(this.terminalWidth)); line.appendChild(span); } - line.style.height = this.cursorHeight + 'px'; - var console = this.console[this.currentScreen]; + line.style.height = this.cursorHeight + 'px'; + var console = this.console[this.currentScreen]; if (console.childNodes.length > y) { console.insertBefore(line, console.childNodes[y]); } else { @@ -1104,7 +1107,9 @@ VT100.prototype.truncateLines = function(width) { } // Prune white space from the end of the current line var span = line.lastChild; - while (span && !span.style.cssText.length) { + while (span && + span.className == 'ansi0 bgAnsi15' && + !span.style.cssText.length) { // Scan backwards looking for first non-space character var s = this.getTextContent(span); for (var i = s.length; i--; ) { @@ -1135,7 +1140,10 @@ VT100.prototype.truncateLines = function(width) { } }; -VT100.prototype.putString = function(x, y, text, style) { +VT100.prototype.putString = function(x, y, text, color, style) { + if (!color) { + color = 'ansi0 bgAnsi15'; + } if (!style) { style = ''; } @@ -1192,12 +1200,15 @@ VT100.prototype.putString = function(x, y, text, style) { // If current is not long enough, pad with spaces or add new // span s = this.getTextContent(span); + var oldColor = span.className; var oldStyle = span.style.cssText; if (xPos + s.length < x) { - if (oldStyle != '') { + if (oldColor != 'ansi0 bgAnsi15' || oldStyle != '') { span = document.createElement('span'); line.appendChild(span); + span.className = 'ansi0 bgAnsi15'; span.style.cssText = ''; + oldColor = 'ansi0 bgAnsi15'; oldStyle = ''; xPos += s.length; s = ''; @@ -1209,7 +1220,8 @@ VT100.prototype.putString = function(x, y, text, style) { // If styles do not match, create a new var del = text.length - s.length + x - xPos; - if (oldStyle != style && (oldStyle || style)) { + if (oldColor != color || + (oldStyle != style && (oldStyle || style))) { if (xPos == x) { // Replacing text at beginning of existing if (text.length >= s.length) { @@ -1236,6 +1248,7 @@ VT100.prototype.putString = function(x, y, text, style) { span = sibling; if (remainder.length) { sibling = document.createElement('span'); + sibling.className = oldColor; sibling.style.cssText = oldStyle; this.setTextContent(sibling, remainder); line.insertBefore(sibling, span.nextSibling); @@ -1245,6 +1258,7 @@ VT100.prototype.putString = function(x, y, text, style) { span = sibling; if (remainder.length) { sibling = document.createElement('span'); + sibling.className = oldColor; sibling.style.cssText = oldStyle; this.setTextContent(sibling, remainder); line.appendChild(sibling); @@ -1252,6 +1266,7 @@ VT100.prototype.putString = function(x, y, text, style) { } s = text; } + span.className = color; span.style.cssText = style; } else { // Overwrite (partial) with new text @@ -1278,7 +1293,8 @@ VT100.prototype.putString = function(x, y, text, style) { } // Merge with next sibling, if styles are identical - if (sibling && span.style.cssText == sibling.style.cssText) { + if (sibling && span.className == sibling.className && + span.style.cssText == sibling.style.cssText) { this.setTextContent(span, this.getTextContent(span) + this.getTextContent(sibling)); @@ -1348,6 +1364,7 @@ VT100.prototype.putString = function(x, y, text, style) { if (text.length) { // Merge with previous sibling, if styles are identical if ((sibling = span.previousSibling) && + span.className == sibling.className && span.style.cssText == sibling.style.cssText) { this.setTextContent(span, this.getTextContent(sibling) + @@ -1357,7 +1374,9 @@ VT100.prototype.putString = function(x, y, text, style) { // Prune white space from the end of the current line span = line.lastChild; - while (span && !span.style.cssText.length) { + while (span && + span.className == 'ansi0 bgAnsi15' && + !span.style.cssText.length) { // Scan backwards looking for first non-space character s = this.getTextContent(span); for (var i = s.length; i--; ) { @@ -1418,11 +1437,10 @@ VT100.prototype.gotoXaY = function(x, y) { VT100.prototype.refreshInvertedState = function() { if (this.isInverted) { - this.scrollable.style.color = this.ansi[15]; - this.scrollable.style.backgroundColor = this.ansi[0]; + this.scrollable.className += ' inverted'; } else { - this.scrollable.style.color = ''; - this.scrollable.style.backgroundColor = ''; + this.scrollable.className = this.scrollable.className. + replace(/ *inverted/, ''); } }; @@ -1502,7 +1520,7 @@ VT100.prototype.spaces = function(i) { return s; }; -VT100.prototype.clearRegion = function(x, y, w, h, style) { +VT100.prototype.clearRegion = function(x, y, w, h, color, style) { w += x; if (x < 0) { x = 0; @@ -1529,7 +1547,7 @@ VT100.prototype.clearRegion = function(x, y, w, h, style) { // child nodes. if (!this.numScrollbackLines && w == this.terminalWidth && h == this.terminalHeight && - !style) { + (color == undefined || color == 'ansi0 bgAnsi15') && !style) { var console = this.console[this.currentScreen]; while (console.lastChild) { console.removeChild(console.lastChild); @@ -1541,54 +1559,66 @@ VT100.prototype.clearRegion = function(x, y, w, h, style) { var cy = this.cursorY; var s = this.spaces(w); for (var i = y+h; i-- > y; ) { - this.putString(x, i, s, style); + this.putString(x, i, s, color, style); } hidden ? this.showCursor(cx, cy) : this.putString(cx, cy, '', undefined); } }; VT100.prototype.copyLineSegment = function(dX, dY, sX, sY, w) { - var text = [ ]; - var style = [ ]; - var console = this.console[this.currentScreen]; + var text = [ ]; + var className = [ ]; + var style = [ ]; + var console = this.console[this.currentScreen]; if (sY >= console.childNodes.length) { - text[0] = this.spaces(w); - style[0] = null; + text[0] = this.spaces(w); + className[0] = undefined; + style[0] = undefined; } else { var line = console.childNodes[sY]; if (line.tagName != 'DIV' || !line.childNodes.length) { - text[0] = this.spaces(w); - style[0] = null; + text[0] = this.spaces(w); + className[0] = undefined; + style[0] = undefined; } else { - var x = 0; + var x = 0; for (var span = line.firstChild; span && w > 0; span = span.nextSibling){ - var s = this.getTextContent(span); - var len = s.length; + var s = this.getTextContent(span); + var len = s.length; if (x + len > sX) { - var o = sX > x ? sX - x : 0; - text[text.length] = s.substr(o, w); - style[style.length] = span.style.cssText; - w -= len - o; + var o = sX > x ? sX - x : 0; + text[text.length] = s.substr(o, w); + className[className.length] = span.className; + style[style.length] = span.style.cssText; + w -= len - o; } - x += len; + x += len; } if (w > 0) { - text[text.length] = this.spaces(w); - style[style.length] = null; + text[text.length] = this.spaces(w); + className[className.length] = undefined; + style[style.length] = undefined; } } } - var hidden = this.hideCursor(); - var cx = this.cursorX; - var cy = this.cursorY; + var hidden = this.hideCursor(); + var cx = this.cursorX; + var cy = this.cursorY; for (var i = 0; i < text.length; i++) { - this.putString(dX, dY - this.numScrollbackLines, text[i], style[i]); - dX += text[i].length; + var color; + if (className[i]) { + color = className[i]; + } else { + color = 'ansi0 bgAnsi15'; + } + this.putString(dX, dY - this.numScrollbackLines, text[i], color, style[i]); + dX += text[i].length; } hidden ? this.showCursor(cx, cy) : this.putString(cx, cy, '', undefined); }; -VT100.prototype.scrollRegion = function(x, y, w, h, incX, incY, style) { +VT100.prototype.scrollRegion = function(x, y, w, h, incX, incY, + color, style) { var left = incX < 0 ? -incX : 0; var right = incX > 0 ? incX : 0; var up = incY < 0 ? -incY : 0; @@ -1623,9 +1653,7 @@ VT100.prototype.scrollRegion = function(x, y, w, h, incX, incY, style) { // fill with underlined spaces. N.B. this is different from the // cases when the user blanks a region. User-initiated blanking // always fills with all of the current attributes. - this.attributeHelper.cssText - = style.replace(/text-decoration:underline;/, ""); - style = this.attributeHelper.cssText; + style = style.replace(/text-decoration:underline;/, ''); } // Compute current scroll position @@ -1654,7 +1682,7 @@ VT100.prototype.scrollRegion = function(x, y, w, h, incX, incY, style) { // Add new lines at bottom in order to force scrolling for (var i = 0; i < y; i++) { - this.insertBlankLine(console.childNodes.length, style); + this.insertBlankLine(console.childNodes.length, color, style); } // Adjust the number of lines in the scrollback buffer by @@ -1691,7 +1719,7 @@ VT100.prototype.scrollRegion = function(x, y, w, h, incX, incY, style) { console.childNodes.length > this.numScrollbackLines+y+h+incY) { for (var i = -incY; i-- > 0; ) { this.insertBlankLine(this.numScrollbackLines + y + h + incY, - style); + color, style); } } } @@ -1703,7 +1731,7 @@ VT100.prototype.scrollRegion = function(x, y, w, h, incX, incY, style) { console.removeChild(console.childNodes[this.numScrollbackLines+y+h]); } for (var i = incY; i--; ) { - this.insertBlankLine(this.numScrollbackLines + y, style); + this.insertBlankLine(this.numScrollbackLines + y, color, style); } } } else { @@ -1725,14 +1753,14 @@ VT100.prototype.scrollRegion = function(x, y, w, h, incX, incY, style) { // Clear blank regions if (incX > 0) { - this.clearRegion(x, y, incX, h, style); + this.clearRegion(x, y, incX, h, color, style); } else if (incX < 0) { - this.clearRegion(x + w + incX, y, -incX, h, style); + this.clearRegion(x + w + incX, y, -incX, h, color, style); } if (incY > 0) { - this.clearRegion(x, y, w, incY, style); + this.clearRegion(x, y, w, incY, color, style); } else if (incY < 0) { - this.clearRegion(x, y + h + incY, w, -incY, style); + this.clearRegion(x, y + h + incY, w, -incY, color, style); } } @@ -1801,7 +1829,7 @@ VT100.prototype.toggleBell = function() { }; VT100.prototype.about = function() { - alert("VT100 Terminal Emulator " + "2.9 (revision 166)" + + alert("VT100 Terminal Emulator " + "2.9 (revision 167)" + "\nCopyright 2008-2009 by Markus Gutschke\n" + "For more information check http://shellinabox.com"); }; @@ -1829,9 +1857,11 @@ VT100.prototype.showContextMenu = function(x, y) { '
  • Reset
  • ' + '
    ' + '
  • ' + - (this.utfEnabled ? '✔ ' : '') + 'Unicode
  • ' + + (this.utfEnabled ? '' : '') + + 'Unicode' + '
  • ' + - (this.visualBell ? '✔ ' : '') + 'Visual Bell
  • '+ + (this.visualBell ? '' : '') + + 'Visual Bell'+ (this.usercss.firstChild ? '
    ' + this.usercss.innerHTML + @@ -2576,7 +2606,7 @@ VT100.prototype.lf = function(count) { if (this.cursorY == this.bottom - 1) { this.scrollRegion(0, this.top + 1, this.terminalWidth, this.bottom - this.top - 1, - 0, -1, this.style); + 0, -1, this.color, this.style); offset = undefined; } else if (this.cursorY < this.terminalHeight - 1) { this.gotoXY(this.cursorX, this.cursorY + 1); @@ -2599,7 +2629,7 @@ VT100.prototype.ri = function(count) { if (this.cursorY == this.top) { this.scrollRegion(0, this.top, this.terminalWidth, this.bottom - this.top - 1, - 0, 1, this.style); + 0, 1, this.color, this.style); } else if (this.cursorY > 0) { this.gotoXY(this.cursorX, this.cursorY - 1); } @@ -2615,48 +2645,42 @@ VT100.prototype.respondSecondaryDA = function() { this.respondString += '\u001B[>0;0;0c'; }; + VT100.prototype.updateStyle = function() { - var style = ''; + this.style = ''; if (this.attr & 0x0200 /* ATTR_UNDERLINE */) { - style += 'text-decoration:underline;'; + this.style = 'text-decoration:underline;'; } - var bg = (this.attr >> 4) & 0xF; - var fg = this.attr & 0xF; + var bg = (this.attr >> 4) & 0xF; + var fg = this.attr & 0xF; if (this.attr & 0x0100 /* ATTR_REVERSE */) { - var tmp = bg; - bg = fg; - fg = tmp; + var tmp = bg; + bg = fg; + fg = tmp; } if ((this.attr & (0x0100 /* ATTR_REVERSE */ | 0x0400 /* ATTR_DIM */)) == 0x0400 /* ATTR_DIM */) { - fg = 8; // Dark grey + fg = 8; // Dark grey } else if (this.attr & 0x0800 /* ATTR_BRIGHT */) { - fg |= 8; + fg |= 8; } if (this.attr & 0x1000 /* ATTR_BLINK */) { - bg ^= 8; + bg ^= 8; } // Make some readability enhancements. Most notably, disallow identical // background and foreground colors. if (bg == fg) { - if ((fg ^= 8) == 7) { - fg = 8; + if ((fg ^= 8) == 7) { + fg = 8; } } // And disallow bright colors on a light-grey background. if (bg == 7 && fg >= 8) { - if ((fg -= 8) == 7) { - fg = 8; + if ((fg -= 8) == 7) { + fg = 8; } } - if (fg != 0) { - style += 'color:' + this.ansi[fg] + ';'; - } - if (bg != 15) { - style += 'background-color:' + this.ansi[bg] + ';'; - } - this.attributeHelper.cssText = style; - this.style = this.attributeHelper.cssText; + this.color = 'ansi' + fg + ' bgAnsi' + bg; }; VT100.prototype.setAttrColors = function(attr) { @@ -2750,7 +2774,7 @@ VT100.prototype.csiAt = function(number) { } this.scrollRegion(this.cursorX, this.cursorY, this.terminalWidth - this.cursorX - number, 1, - number, 0, this.style); + number, 0, this.color, this.style); this.needWrap = false; }; @@ -2758,22 +2782,26 @@ VT100.prototype.csiJ = function(number) { switch (number) { case 0: // Erase from cursor to end of display this.clearRegion(this.cursorX, this.cursorY, - this.terminalWidth - this.cursorX, 1, this.style); + this.terminalWidth - this.cursorX, 1, + this.color, this.style); if (this.cursorY < this.terminalHeight-2) { this.clearRegion(0, this.cursorY+1, this.terminalWidth, this.terminalHeight-this.cursorY-1, - this.style); + this.color, this.style); } break; case 1: // Erase from start to cursor if (this.cursorY > 0) { this.clearRegion(0, 0, - this.terminalWidth, this.cursorY, this.style); + this.terminalWidth, this.cursorY, + this.color, this.style); } - this.clearRegion(0, this.cursorY, this.cursorX + 1, 1, this.style); + this.clearRegion(0, this.cursorY, this.cursorX + 1, 1, + this.color, this.style); break; case 2: // Erase whole display - this.clearRegion(0, 0, this.terminalWidth, this.terminalHeight,this.style); + this.clearRegion(0, 0, this.terminalWidth, this.terminalHeight, + this.color, this.style); break; default: return; @@ -2785,13 +2813,16 @@ VT100.prototype.csiK = function(number) { switch (number) { case 0: // Erase from cursor to end of line this.clearRegion(this.cursorX, this.cursorY, - this.terminalWidth - this.cursorX, 1, this.style); + this.terminalWidth - this.cursorX, 1, + this.color, this.style); break; case 1: // Erase from start of line to cursor - this.clearRegion(0, this.cursorY, this.cursorX + 1, 1, this.style); + this.clearRegion(0, this.cursorY, this.cursorX + 1, 1, + this.color, this.style); break; case 2: // Erase whole line - this.clearRegion(0, this.cursorY, this.terminalWidth, 1, this.style); + this.clearRegion(0, this.cursorY, this.terminalWidth, 1, + this.color, this.style); break; default: return; @@ -2812,7 +2843,7 @@ VT100.prototype.csiL = function(number) { } this.scrollRegion(0, this.cursorY, this.terminalWidth, this.bottom - this.cursorY - number, - 0, number, this.style); + 0, number, this.color, this.style); needWrap = false; }; @@ -2829,7 +2860,7 @@ VT100.prototype.csiM = function(number) { } this.scrollRegion(0, this.cursorY + number, this.terminalWidth, this.bottom - this.cursorY - number, - 0, -number, this.style); + 0, -number, this.color, this.style); needWrap = false; }; @@ -2890,7 +2921,7 @@ VT100.prototype.csiP = function(number) { } this.scrollRegion(this.cursorX + number, this.cursorY, this.terminalWidth - this.cursorX - number, 1, - -number, 0, this.style); + -number, 0, this.color, this.style); needWrap = false; }; @@ -2902,7 +2933,8 @@ VT100.prototype.csiX = function(number) { if (number > this.terminalWidth - this.cursorX) { number = this.terminalWidth - this.cursorX; } - this.clearRegion(this.cursorX, this.cursorY, number, 1, this.style); + this.clearRegion(this.cursorX, this.cursorY, number, 1, + this.color, this.style); needWrap = false; }; @@ -3224,7 +3256,7 @@ VT100.prototype.renderString = function(s, showCursor) { // call to this.showCursor() this.cursor.style.visibility = ''; } - this.putString(this.cursorX, this.cursorY, s, this.style); + this.putString(this.cursorX, this.cursorY, s, this.color, this.style); }; VT100.prototype.vt100 = function(s) { @@ -3299,7 +3331,7 @@ VT100.prototype.vt100 = function(s) { if (this.insertMode) { this.scrollRegion(this.cursorX, this.cursorY, this.terminalWidth - this.cursorX - 1, 1, - 1, 0, this.style); + 1, 0, this.color, this.style); } this.lastCharacter = String.fromCharCode(ch); lineBuf += this.lastCharacter; diff --git a/shellinabox/black-on-white.css b/shellinabox/black-on-white.css new file mode 100644 index 0000000..e69de29 diff --git a/shellinabox/color.css b/shellinabox/color.css new file mode 100644 index 0000000..e69de29 diff --git a/shellinabox/enabled.gif b/shellinabox/enabled.gif new file mode 100644 index 0000000..07936e2 Binary files /dev/null and b/shellinabox/enabled.gif differ diff --git a/shellinabox/launcher.c b/shellinabox/launcher.c index 47732ee..26976fb 100644 --- a/shellinabox/launcher.c +++ b/shellinabox/launcher.c @@ -1195,12 +1195,24 @@ static void execService(int width, int height, struct Service *service, void setWindowSize(int pty, int width, int height) { if (width > 0 && height > 0) { - struct winsize win; - win.ws_row = height; - win.ws_col = width; - win.ws_xpixel = 0; - win.ws_ypixel = 0; - ioctl(pty, TIOCSWINSZ, &win); + #ifdef TIOCSSIZE + { + struct ttysize win; + ioctl(pty, TIOCGSIZE, &win); + win.ts_lines = height; + win.ts_cols = width; + ioctl(pty, TIOCSSIZE, &win); + } + #endif + #ifdef TIOCGWINSZ + { + struct winsize win; + ioctl(pty, TIOCGWINSZ, &win); + win.ws_row = height; + win.ws_col = width; + ioctl(pty, TIOCSWINSZ, &win); + } + #endif } } diff --git a/shellinabox/monochrome.css b/shellinabox/monochrome.css new file mode 100644 index 0000000..b5c60a5 --- /dev/null +++ b/shellinabox/monochrome.css @@ -0,0 +1,29 @@ +#vt100 .ansi1 { color: inherit; } +#vt100 .ansi2 { color: inherit; } +#vt100 .ansi3 { color: inherit; } +#vt100 .ansi4 { color: inherit; } +#vt100 .ansi5 { color: inherit; } +#vt100 .ansi6 { color: inherit; } +#vt100 .ansi7 { color: inherit; } +#vt100 .ansi8 { color: inherit; } +#vt100 .ansi9 { color: inherit; } +#vt100 .ansi10 { color: inherit; } +#vt100 .ansi11 { color: inherit; } +#vt100 .ansi12 { color: inherit; } +#vt100 .ansi13 { color: inherit; } +#vt100 .ansi14 { color: inherit; } + +#vt100 .bgAnsi1 { background-color: inherit; } +#vt100 .bgAnsi2 { background-color: inherit; } +#vt100 .bgAnsi3 { background-color: inherit; } +#vt100 .bgAnsi4 { background-color: inherit; } +#vt100 .bgAnsi5 { background-color: inherit; } +#vt100 .bgAnsi6 { background-color: inherit; } +#vt100 .bgAnsi7 { background-color: inherit; } +#vt100 .bgAnsi8 { background-color: inherit; } +#vt100 .bgAnsi9 { background-color: inherit; } +#vt100 .bgAnsi10 { background-color: inherit; } +#vt100 .bgAnsi11 { background-color: inherit; } +#vt100 .bgAnsi12 { background-color: inherit; } +#vt100 .bgAnsi13 { background-color: inherit; } +#vt100 .bgAnsi14 { background-color: inherit; } diff --git a/shellinabox/shell_in_a_box.js b/shellinabox/shell_in_a_box.js index 7b8c872..a6cb725 100644 --- a/shellinabox/shell_in_a_box.js +++ b/shellinabox/shell_in_a_box.js @@ -355,7 +355,7 @@ ShellInABox.prototype.extendContextMenu = function(entries, actions) { }; ShellInABox.prototype.about = function() { - alert("Shell In A Box version " + "2.9 (revision 166)" + + alert("Shell In A Box version " + "2.9 (revision 167)" + "\nCopyright 2008-2009 by Markus Gutschke\n" + "For more information check http://shellinabox.com" + (typeof serverSupportsSSL != 'undefined' && serverSupportsSSL ? diff --git a/shellinabox/shellinaboxd.c b/shellinabox/shellinaboxd.c index e077b28..b89669a 100644 --- a/shellinabox/shellinaboxd.c +++ b/shellinabox/shellinaboxd.c @@ -507,6 +507,11 @@ static int shellInABoxHttpHandler(HttpConnection *http, void *arg, extern char beepStart[]; extern char beepEnd[]; serveStaticFile(http, "audio/x-wav", beepStart, beepEnd); + } else if (pathInfoLength == 11 && !memcmp(pathInfo, "enabled.gif", 11)) { + // Serve the checkmark icon used in the context menu + extern char enabledStart[]; + extern char enabledEnd[]; + serveStaticFile(http, "image/gif", enabledStart, enabledEnd); } else if (pathInfoLength == 11 && !memcmp(pathInfo, "favicon.ico", 11)) { // Serve the favicon extern char faviconStart[]; diff --git a/shellinabox/styles.css b/shellinabox/styles.css index ab66973..6e465a1 100644 --- a/shellinabox/styles.css +++ b/shellinabox/styles.css @@ -127,22 +127,47 @@ margin: 0.5ex 0px 0.5ex 0px; } -#vt100 #ansi0 { background-color: #000000; } -#vt100 #ansi1 { background-color: #cd0000; } -#vt100 #ansi2 { background-color: #00cd00; } -#vt100 #ansi3 { background-color: #cdcd00; } -#vt100 #ansi4 { background-color: #0000ee; } -#vt100 #ansi5 { background-color: #cd00cd; } -#vt100 #ansi6 { background-color: #00cdcd; } -#vt100 #ansi7 { background-color: #e5e5e5; } -#vt100 #ansi8 { background-color: #7f7f7f; } -#vt100 #ansi9 { background-color: #ff0000; } -#vt100 #ansi10 { background-color: #00ff00; } -#vt100 #ansi11 { background-color: #e8e800; } -#vt100 #ansi12 { background-color: #5c5cff; } -#vt100 #ansi13 { background-color: #ff00ff; } -#vt100 #ansi14 { background-color: #00ffff; } -#vt100 #ansi15 { background-color: #ffffff; } +#vt100 #menu img { + margin-right: 0.5ex; + width: 1ex; + height: 1ex; +} + +#vt100 #scrollable.inverted { color: #ffffff; + background-color: #000000; } +#vt100 .ansi0 { } +#vt100 .ansi1 { color: #cd0000; } +#vt100 .ansi2 { color: #00cd00; } +#vt100 .ansi3 { color: #cdcd00; } +#vt100 .ansi4 { color: #0000ee; } +#vt100 .ansi5 { color: #cd00cd; } +#vt100 .ansi6 { color: #00cdcd; } +#vt100 .ansi7 { color: #e5e5e5; } +#vt100 .ansi8 { color: #7f7f7f; } +#vt100 .ansi9 { color: #ff0000; } +#vt100 .ansi10 { color: #00ff00; } +#vt100 .ansi11 { color: #e8e800; } +#vt100 .ansi12 { color: #5c5cff; } +#vt100 .ansi13 { color: #ff00ff; } +#vt100 .ansi14 { color: #00ffff; } +#vt100 .ansi15 { color: #ffffff; } + +#vt100 .bgAnsi0 { background-color: #000000; } +#vt100 .bgAnsi1 { background-color: #cd0000; } +#vt100 .bgAnsi2 { background-color: #00cd00; } +#vt100 .bgAnsi3 { background-color: #cdcd00; } +#vt100 .bgAnsi4 { background-color: #0000ee; } +#vt100 .bgAnsi5 { background-color: #cd00cd; } +#vt100 .bgAnsi6 { background-color: #00cdcd; } +#vt100 .bgAnsi7 { background-color: #e5e5e5; } +#vt100 .bgAnsi8 { background-color: #7f7f7f; } +#vt100 .bgAnsi9 { background-color: #ff0000; } +#vt100 .bgAnsi10 { background-color: #00ff00; } +#vt100 .bgAnsi11 { background-color: #e8e800; } +#vt100 .bgAnsi12 { background-color: #5c5cff; } +#vt100 .bgAnsi13 { background-color: #ff00ff; } +#vt100 .bgAnsi14 { background-color: #00ffff; } +#vt100 .bgAnsi15 { } @media print { #vt100 .scrollback { diff --git a/shellinabox/vt100.js b/shellinabox/vt100.js index 15a555b..96fcc2b 100644 --- a/shellinabox/vt100.js +++ b/shellinabox/vt100.js @@ -174,7 +174,6 @@ function VT100(container) { '(?:[?](?:(?![ \u00A0]|[,.)}"\u0027!]+[ \u00A0]|[,.)}"\u0027!]+$).)*)?'); } this.initializeElements(container); - this.initializeAnsiColors(); this.maxScrollbackLines = 500; this.npar = 0; this.par = [ ]; @@ -210,6 +209,7 @@ VT100.prototype.reset = function(clearHistory) { suppressAllAudio; this.utfCount = 0; this.utfChar = 0; + this.color = 'ansi0 bgAnsi15'; this.style = ''; this.attr = 0x00F0 /* ATTR_DEFAULT */; this.useGMap = 0; @@ -236,19 +236,8 @@ VT100.prototype.reset = function(clearHistory) { this.showCursor(); this.isInverted = false; this.refreshInvertedState(); - this.clearRegion(0, 0, this.terminalWidth, this.terminalHeight, this.style); -}; - -VT100.prototype.initializeAnsiColors = function() { - var elem = document.createElement('pre'); - this.container.appendChild(elem); - this.setTextContent(elem, ' '); - this.ansi = [ ]; - for (var i = 0; i < 16; i++) { - elem.id = 'ansi' + i; - this.ansi[i] = this.getCurrentComputedStyle(elem, 'backgroundColor'); - } - this.container.removeChild(elem); + this.clearRegion(0, 0, this.terminalWidth, this.terminalHeight, + this.color, this.style); }; VT100.prototype.addListener = function(elem, event, listener) { @@ -319,8 +308,17 @@ VT100.prototype.initializeUserCSSStyles = function() { if (++i >= begin) { --c; var label = vt100.usercss.childNodes[j]; - label.innerHTML = - label.innerHTML.replace(/^\u2714 /, ''); + + // Restore label to just the text content + if (typeof label.textContent == 'undefined') { + var s = label.innerText; + label.innerHTML = ''; + label.appendChild(document.createTextNode(s)); + } else { + label.textContent= label.textContent; + } + + // User style sheets are number sequentially var sheet = document.getElementById( 'usercss-' + i); if (i == current) { @@ -330,7 +328,8 @@ VT100.prototype.initializeUserCSSStyles = function() { sheet.disabled = false; } if (!sheet.disabled) { - label.innerHTML= '✔ ' + label.innerHTML; + label.innerHTML= '' + + label.innerHTML; } } else { sheet.disabled = true; @@ -355,7 +354,9 @@ VT100.prototype.initializeUserCSSStyles = function() { // both ends), or whether this is a on/off toggle, which can be grouped // together with other on/off options. group += - '
  • ' + (enabled ? '✔ ' : '') + label + '
  • '; + '
  • ' + (enabled ? '' : '') + + label + + '
  • '; } this.usercss.innerHTML = menu; } @@ -384,8 +385,7 @@ VT100.prototype.initializeElements = function(container) { !this.getChildById(this.container, 'usercss') || !this.getChildById(this.container, 'space') || !this.getChildById(this.container, 'input') || - !this.getChildById(this.container, 'cliphelper') || - !this.getChildById(this.container, 'attrib')) { + !this.getChildById(this.container, 'cliphelper')) { // Only enable the "embed" object, if we have a suitable plugin. Otherwise, // we might get a pointless warning that a suitable plugin is not yet // installed. If in doubt, we'd rather just stay silent. @@ -432,7 +432,6 @@ VT100.prototype.initializeElements = function(container) { '
    ' + '' + '' + - ' ' + (typeof suppressAllAudio != 'undefined' && suppressAllAudio ? "" : embed + '') + @@ -474,7 +473,6 @@ VT100.prototype.initializeElements = function(container) { 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(); @@ -636,12 +634,13 @@ VT100.prototype.repairElements = function(console) { for (var line = console.firstChild; line; line = line.nextSibling) { if (!line.clientHeight) { var newLine = document.createElement(line.tagName); - newLine.style.cssText = line.style.cssText; - newLine.className = line.className; + newLine.style.cssText = line.style.cssText; + newLine.className = line.className; if (line.tagName == 'DIV') { for (var span = line.firstChild; span; span = span.nextSibling) { - var newSpan = document.createElement(span.tagName); - newSpan.style.cssText = span.style.cssText; + var newSpan = document.createElement(span.tagName); + newSpan.style.cssText = span.style.cssText; + newSpan.style.className = span.style.className; this.setTextContent(newSpan, this.getTextContent(span)); newLine.appendChild(newSpan); } @@ -649,7 +648,7 @@ VT100.prototype.repairElements = function(console) { this.setTextContent(newLine, this.getTextContent(line)); } line.parentNode.replaceChild(newLine, line); - line = newLine; + line = newLine; } } }; @@ -1015,7 +1014,7 @@ VT100.prototype.setTextContent = function(elem, s) { } }; -VT100.prototype.insertBlankLine = function(y, style) { +VT100.prototype.insertBlankLine = function(y, color, style) { // Insert a blank line a position y. This method ignores the scrollback // buffer. The caller has to add the length of the scrollback buffer to // the position, if necessary. @@ -1023,22 +1022,26 @@ VT100.prototype.insertBlankLine = function(y, style) { // method just adds a new line right after the last existing one. It does // not add any missing lines in between. It is the caller's responsibility // to do so. - if (style == undefined) { - style = ''; + if (!color) { + color = 'ansi0 bgAnsi15'; + } + if (!style) { + style = ''; } var line; - if (!style) { - line = document.createElement('pre'); + if (color != 'ansi0 bgAnsi15' && !style) { + line = document.createElement('pre'); this.setTextContent(line, '\n'); } else { - line = document.createElement('div'); - var span = document.createElement('span'); - span.style.cssText = style; + line = document.createElement('div'); + var span = document.createElement('span'); + span.style.cssText = style; + span.style.className = color; this.setTextContent(span, this.spaces(this.terminalWidth)); line.appendChild(span); } - line.style.height = this.cursorHeight + 'px'; - var console = this.console[this.currentScreen]; + line.style.height = this.cursorHeight + 'px'; + var console = this.console[this.currentScreen]; if (console.childNodes.length > y) { console.insertBefore(line, console.childNodes[y]); } else { @@ -1104,7 +1107,9 @@ VT100.prototype.truncateLines = function(width) { } // Prune white space from the end of the current line var span = line.lastChild; - while (span && !span.style.cssText.length) { + while (span && + span.className == 'ansi0 bgAnsi15' && + !span.style.cssText.length) { // Scan backwards looking for first non-space character var s = this.getTextContent(span); for (var i = s.length; i--; ) { @@ -1135,7 +1140,10 @@ VT100.prototype.truncateLines = function(width) { } }; -VT100.prototype.putString = function(x, y, text, style) { +VT100.prototype.putString = function(x, y, text, color, style) { + if (!color) { + color = 'ansi0 bgAnsi15'; + } if (!style) { style = ''; } @@ -1192,12 +1200,15 @@ VT100.prototype.putString = function(x, y, text, style) { // If current is not long enough, pad with spaces or add new // span s = this.getTextContent(span); + var oldColor = span.className; var oldStyle = span.style.cssText; if (xPos + s.length < x) { - if (oldStyle != '') { + if (oldColor != 'ansi0 bgAnsi15' || oldStyle != '') { span = document.createElement('span'); line.appendChild(span); + span.className = 'ansi0 bgAnsi15'; span.style.cssText = ''; + oldColor = 'ansi0 bgAnsi15'; oldStyle = ''; xPos += s.length; s = ''; @@ -1209,7 +1220,8 @@ VT100.prototype.putString = function(x, y, text, style) { // If styles do not match, create a new var del = text.length - s.length + x - xPos; - if (oldStyle != style && (oldStyle || style)) { + if (oldColor != color || + (oldStyle != style && (oldStyle || style))) { if (xPos == x) { // Replacing text at beginning of existing if (text.length >= s.length) { @@ -1236,6 +1248,7 @@ VT100.prototype.putString = function(x, y, text, style) { span = sibling; if (remainder.length) { sibling = document.createElement('span'); + sibling.className = oldColor; sibling.style.cssText = oldStyle; this.setTextContent(sibling, remainder); line.insertBefore(sibling, span.nextSibling); @@ -1245,6 +1258,7 @@ VT100.prototype.putString = function(x, y, text, style) { span = sibling; if (remainder.length) { sibling = document.createElement('span'); + sibling.className = oldColor; sibling.style.cssText = oldStyle; this.setTextContent(sibling, remainder); line.appendChild(sibling); @@ -1252,6 +1266,7 @@ VT100.prototype.putString = function(x, y, text, style) { } s = text; } + span.className = color; span.style.cssText = style; } else { // Overwrite (partial) with new text @@ -1278,7 +1293,8 @@ VT100.prototype.putString = function(x, y, text, style) { } // Merge with next sibling, if styles are identical - if (sibling && span.style.cssText == sibling.style.cssText) { + if (sibling && span.className == sibling.className && + span.style.cssText == sibling.style.cssText) { this.setTextContent(span, this.getTextContent(span) + this.getTextContent(sibling)); @@ -1348,6 +1364,7 @@ VT100.prototype.putString = function(x, y, text, style) { if (text.length) { // Merge with previous sibling, if styles are identical if ((sibling = span.previousSibling) && + span.className == sibling.className && span.style.cssText == sibling.style.cssText) { this.setTextContent(span, this.getTextContent(sibling) + @@ -1357,7 +1374,9 @@ VT100.prototype.putString = function(x, y, text, style) { // Prune white space from the end of the current line span = line.lastChild; - while (span && !span.style.cssText.length) { + while (span && + span.className == 'ansi0 bgAnsi15' && + !span.style.cssText.length) { // Scan backwards looking for first non-space character s = this.getTextContent(span); for (var i = s.length; i--; ) { @@ -1418,11 +1437,10 @@ VT100.prototype.gotoXaY = function(x, y) { VT100.prototype.refreshInvertedState = function() { if (this.isInverted) { - this.scrollable.style.color = this.ansi[15]; - this.scrollable.style.backgroundColor = this.ansi[0]; + this.scrollable.className += ' inverted'; } else { - this.scrollable.style.color = ''; - this.scrollable.style.backgroundColor = ''; + this.scrollable.className = this.scrollable.className. + replace(/ *inverted/, ''); } }; @@ -1502,7 +1520,7 @@ VT100.prototype.spaces = function(i) { return s; }; -VT100.prototype.clearRegion = function(x, y, w, h, style) { +VT100.prototype.clearRegion = function(x, y, w, h, color, style) { w += x; if (x < 0) { x = 0; @@ -1529,7 +1547,7 @@ VT100.prototype.clearRegion = function(x, y, w, h, style) { // child nodes. if (!this.numScrollbackLines && w == this.terminalWidth && h == this.terminalHeight && - !style) { + (color == undefined || color == 'ansi0 bgAnsi15') && !style) { var console = this.console[this.currentScreen]; while (console.lastChild) { console.removeChild(console.lastChild); @@ -1541,54 +1559,66 @@ VT100.prototype.clearRegion = function(x, y, w, h, style) { var cy = this.cursorY; var s = this.spaces(w); for (var i = y+h; i-- > y; ) { - this.putString(x, i, s, style); + this.putString(x, i, s, color, style); } hidden ? this.showCursor(cx, cy) : this.putString(cx, cy, '', undefined); } }; VT100.prototype.copyLineSegment = function(dX, dY, sX, sY, w) { - var text = [ ]; - var style = [ ]; - var console = this.console[this.currentScreen]; + var text = [ ]; + var className = [ ]; + var style = [ ]; + var console = this.console[this.currentScreen]; if (sY >= console.childNodes.length) { - text[0] = this.spaces(w); - style[0] = null; + text[0] = this.spaces(w); + className[0] = undefined; + style[0] = undefined; } else { var line = console.childNodes[sY]; if (line.tagName != 'DIV' || !line.childNodes.length) { - text[0] = this.spaces(w); - style[0] = null; + text[0] = this.spaces(w); + className[0] = undefined; + style[0] = undefined; } else { - var x = 0; + var x = 0; for (var span = line.firstChild; span && w > 0; span = span.nextSibling){ - var s = this.getTextContent(span); - var len = s.length; + var s = this.getTextContent(span); + var len = s.length; if (x + len > sX) { - var o = sX > x ? sX - x : 0; - text[text.length] = s.substr(o, w); - style[style.length] = span.style.cssText; - w -= len - o; + var o = sX > x ? sX - x : 0; + text[text.length] = s.substr(o, w); + className[className.length] = span.className; + style[style.length] = span.style.cssText; + w -= len - o; } - x += len; + x += len; } if (w > 0) { - text[text.length] = this.spaces(w); - style[style.length] = null; + text[text.length] = this.spaces(w); + className[className.length] = undefined; + style[style.length] = undefined; } } } - var hidden = this.hideCursor(); - var cx = this.cursorX; - var cy = this.cursorY; + var hidden = this.hideCursor(); + var cx = this.cursorX; + var cy = this.cursorY; for (var i = 0; i < text.length; i++) { - this.putString(dX, dY - this.numScrollbackLines, text[i], style[i]); - dX += text[i].length; + var color; + if (className[i]) { + color = className[i]; + } else { + color = 'ansi0 bgAnsi15'; + } + this.putString(dX, dY - this.numScrollbackLines, text[i], color, style[i]); + dX += text[i].length; } hidden ? this.showCursor(cx, cy) : this.putString(cx, cy, '', undefined); }; -VT100.prototype.scrollRegion = function(x, y, w, h, incX, incY, style) { +VT100.prototype.scrollRegion = function(x, y, w, h, incX, incY, + color, style) { var left = incX < 0 ? -incX : 0; var right = incX > 0 ? incX : 0; var up = incY < 0 ? -incY : 0; @@ -1623,9 +1653,7 @@ VT100.prototype.scrollRegion = function(x, y, w, h, incX, incY, style) { // fill with underlined spaces. N.B. this is different from the // cases when the user blanks a region. User-initiated blanking // always fills with all of the current attributes. - this.attributeHelper.cssText - = style.replace(/text-decoration:underline;/, ""); - style = this.attributeHelper.cssText; + style = style.replace(/text-decoration:underline;/, ''); } // Compute current scroll position @@ -1654,7 +1682,7 @@ VT100.prototype.scrollRegion = function(x, y, w, h, incX, incY, style) { // Add new lines at bottom in order to force scrolling for (var i = 0; i < y; i++) { - this.insertBlankLine(console.childNodes.length, style); + this.insertBlankLine(console.childNodes.length, color, style); } // Adjust the number of lines in the scrollback buffer by @@ -1691,7 +1719,7 @@ VT100.prototype.scrollRegion = function(x, y, w, h, incX, incY, style) { console.childNodes.length > this.numScrollbackLines+y+h+incY) { for (var i = -incY; i-- > 0; ) { this.insertBlankLine(this.numScrollbackLines + y + h + incY, - style); + color, style); } } } @@ -1703,7 +1731,7 @@ VT100.prototype.scrollRegion = function(x, y, w, h, incX, incY, style) { console.removeChild(console.childNodes[this.numScrollbackLines+y+h]); } for (var i = incY; i--; ) { - this.insertBlankLine(this.numScrollbackLines + y, style); + this.insertBlankLine(this.numScrollbackLines + y, color, style); } } } else { @@ -1725,14 +1753,14 @@ VT100.prototype.scrollRegion = function(x, y, w, h, incX, incY, style) { // Clear blank regions if (incX > 0) { - this.clearRegion(x, y, incX, h, style); + this.clearRegion(x, y, incX, h, color, style); } else if (incX < 0) { - this.clearRegion(x + w + incX, y, -incX, h, style); + this.clearRegion(x + w + incX, y, -incX, h, color, style); } if (incY > 0) { - this.clearRegion(x, y, w, incY, style); + this.clearRegion(x, y, w, incY, color, style); } else if (incY < 0) { - this.clearRegion(x, y + h + incY, w, -incY, style); + this.clearRegion(x, y + h + incY, w, -incY, color, style); } } @@ -1801,7 +1829,7 @@ VT100.prototype.toggleBell = function() { }; VT100.prototype.about = function() { - alert("VT100 Terminal Emulator " + "2.9 (revision 166)" + + alert("VT100 Terminal Emulator " + "2.9 (revision 167)" + "\nCopyright 2008-2009 by Markus Gutschke\n" + "For more information check http://shellinabox.com"); }; @@ -1829,9 +1857,11 @@ VT100.prototype.showContextMenu = function(x, y) { '
  • Reset
  • ' + '
    ' + '
  • ' + - (this.utfEnabled ? '✔ ' : '') + 'Unicode
  • ' + + (this.utfEnabled ? '' : '') + + 'Unicode' + '
  • ' + - (this.visualBell ? '✔ ' : '') + 'Visual Bell
  • '+ + (this.visualBell ? '' : '') + + 'Visual Bell'+ (this.usercss.firstChild ? '
    ' + this.usercss.innerHTML + @@ -2576,7 +2606,7 @@ VT100.prototype.lf = function(count) { if (this.cursorY == this.bottom - 1) { this.scrollRegion(0, this.top + 1, this.terminalWidth, this.bottom - this.top - 1, - 0, -1, this.style); + 0, -1, this.color, this.style); offset = undefined; } else if (this.cursorY < this.terminalHeight - 1) { this.gotoXY(this.cursorX, this.cursorY + 1); @@ -2599,7 +2629,7 @@ VT100.prototype.ri = function(count) { if (this.cursorY == this.top) { this.scrollRegion(0, this.top, this.terminalWidth, this.bottom - this.top - 1, - 0, 1, this.style); + 0, 1, this.color, this.style); } else if (this.cursorY > 0) { this.gotoXY(this.cursorX, this.cursorY - 1); } @@ -2615,48 +2645,42 @@ VT100.prototype.respondSecondaryDA = function() { this.respondString += '\u001B[>0;0;0c'; }; + VT100.prototype.updateStyle = function() { - var style = ''; + this.style = ''; if (this.attr & 0x0200 /* ATTR_UNDERLINE */) { - style += 'text-decoration:underline;'; + this.style = 'text-decoration:underline;'; } - var bg = (this.attr >> 4) & 0xF; - var fg = this.attr & 0xF; + var bg = (this.attr >> 4) & 0xF; + var fg = this.attr & 0xF; if (this.attr & 0x0100 /* ATTR_REVERSE */) { - var tmp = bg; - bg = fg; - fg = tmp; + var tmp = bg; + bg = fg; + fg = tmp; } if ((this.attr & (0x0100 /* ATTR_REVERSE */ | 0x0400 /* ATTR_DIM */)) == 0x0400 /* ATTR_DIM */) { - fg = 8; // Dark grey + fg = 8; // Dark grey } else if (this.attr & 0x0800 /* ATTR_BRIGHT */) { - fg |= 8; + fg |= 8; } if (this.attr & 0x1000 /* ATTR_BLINK */) { - bg ^= 8; + bg ^= 8; } // Make some readability enhancements. Most notably, disallow identical // background and foreground colors. if (bg == fg) { - if ((fg ^= 8) == 7) { - fg = 8; + if ((fg ^= 8) == 7) { + fg = 8; } } // And disallow bright colors on a light-grey background. if (bg == 7 && fg >= 8) { - if ((fg -= 8) == 7) { - fg = 8; + if ((fg -= 8) == 7) { + fg = 8; } } - if (fg != 0) { - style += 'color:' + this.ansi[fg] + ';'; - } - if (bg != 15) { - style += 'background-color:' + this.ansi[bg] + ';'; - } - this.attributeHelper.cssText = style; - this.style = this.attributeHelper.cssText; + this.color = 'ansi' + fg + ' bgAnsi' + bg; }; VT100.prototype.setAttrColors = function(attr) { @@ -2750,7 +2774,7 @@ VT100.prototype.csiAt = function(number) { } this.scrollRegion(this.cursorX, this.cursorY, this.terminalWidth - this.cursorX - number, 1, - number, 0, this.style); + number, 0, this.color, this.style); this.needWrap = false; }; @@ -2758,22 +2782,26 @@ VT100.prototype.csiJ = function(number) { switch (number) { case 0: // Erase from cursor to end of display this.clearRegion(this.cursorX, this.cursorY, - this.terminalWidth - this.cursorX, 1, this.style); + this.terminalWidth - this.cursorX, 1, + this.color, this.style); if (this.cursorY < this.terminalHeight-2) { this.clearRegion(0, this.cursorY+1, this.terminalWidth, this.terminalHeight-this.cursorY-1, - this.style); + this.color, this.style); } break; case 1: // Erase from start to cursor if (this.cursorY > 0) { this.clearRegion(0, 0, - this.terminalWidth, this.cursorY, this.style); + this.terminalWidth, this.cursorY, + this.color, this.style); } - this.clearRegion(0, this.cursorY, this.cursorX + 1, 1, this.style); + this.clearRegion(0, this.cursorY, this.cursorX + 1, 1, + this.color, this.style); break; case 2: // Erase whole display - this.clearRegion(0, 0, this.terminalWidth, this.terminalHeight,this.style); + this.clearRegion(0, 0, this.terminalWidth, this.terminalHeight, + this.color, this.style); break; default: return; @@ -2785,13 +2813,16 @@ VT100.prototype.csiK = function(number) { switch (number) { case 0: // Erase from cursor to end of line this.clearRegion(this.cursorX, this.cursorY, - this.terminalWidth - this.cursorX, 1, this.style); + this.terminalWidth - this.cursorX, 1, + this.color, this.style); break; case 1: // Erase from start of line to cursor - this.clearRegion(0, this.cursorY, this.cursorX + 1, 1, this.style); + this.clearRegion(0, this.cursorY, this.cursorX + 1, 1, + this.color, this.style); break; case 2: // Erase whole line - this.clearRegion(0, this.cursorY, this.terminalWidth, 1, this.style); + this.clearRegion(0, this.cursorY, this.terminalWidth, 1, + this.color, this.style); break; default: return; @@ -2812,7 +2843,7 @@ VT100.prototype.csiL = function(number) { } this.scrollRegion(0, this.cursorY, this.terminalWidth, this.bottom - this.cursorY - number, - 0, number, this.style); + 0, number, this.color, this.style); needWrap = false; }; @@ -2829,7 +2860,7 @@ VT100.prototype.csiM = function(number) { } this.scrollRegion(0, this.cursorY + number, this.terminalWidth, this.bottom - this.cursorY - number, - 0, -number, this.style); + 0, -number, this.color, this.style); needWrap = false; }; @@ -2890,7 +2921,7 @@ VT100.prototype.csiP = function(number) { } this.scrollRegion(this.cursorX + number, this.cursorY, this.terminalWidth - this.cursorX - number, 1, - -number, 0, this.style); + -number, 0, this.color, this.style); needWrap = false; }; @@ -2902,7 +2933,8 @@ VT100.prototype.csiX = function(number) { if (number > this.terminalWidth - this.cursorX) { number = this.terminalWidth - this.cursorX; } - this.clearRegion(this.cursorX, this.cursorY, number, 1, this.style); + this.clearRegion(this.cursorX, this.cursorY, number, 1, + this.color, this.style); needWrap = false; }; @@ -3224,7 +3256,7 @@ VT100.prototype.renderString = function(s, showCursor) { // call to this.showCursor() this.cursor.style.visibility = ''; } - this.putString(this.cursorX, this.cursorY, s, this.style); + this.putString(this.cursorX, this.cursorY, s, this.color, this.style); }; VT100.prototype.vt100 = function(s) { @@ -3299,7 +3331,7 @@ VT100.prototype.vt100 = function(s) { if (this.insertMode) { this.scrollRegion(this.cursorX, this.cursorY, this.terminalWidth - this.cursorX - 1, 1, - 1, 0, this.style); + 1, 0, this.color, this.style); } this.lastCharacter = String.fromCharCode(ch); lineBuf += this.lastCharacter; diff --git a/shellinabox/vt100.jspp b/shellinabox/vt100.jspp index 8759856..925ea44 100644 --- a/shellinabox/vt100.jspp +++ b/shellinabox/vt100.jspp @@ -174,7 +174,6 @@ function VT100(container) { '(?:[?](?:(?![ \u00A0]|[,.)}"\u0027!]+[ \u00A0]|[,.)}"\u0027!]+$).)*)?'); } this.initializeElements(container); - this.initializeAnsiColors(); this.maxScrollbackLines = 500; this.npar = 0; this.par = [ ]; @@ -210,6 +209,7 @@ VT100.prototype.reset = function(clearHistory) { suppressAllAudio; this.utfCount = 0; this.utfChar = 0; + this.color = 'ansi0 bgAnsi15'; this.style = ''; this.attr = ATTR_DEFAULT; this.useGMap = 0; @@ -236,19 +236,8 @@ VT100.prototype.reset = function(clearHistory) { this.showCursor(); this.isInverted = false; this.refreshInvertedState(); - this.clearRegion(0, 0, this.terminalWidth, this.terminalHeight, this.style); -}; - -VT100.prototype.initializeAnsiColors = function() { - var elem = document.createElement('pre'); - this.container.appendChild(elem); - this.setTextContent(elem, ' '); - this.ansi = [ ]; - for (var i = 0; i < 16; i++) { - elem.id = 'ansi' + i; - this.ansi[i] = this.getCurrentComputedStyle(elem, 'backgroundColor'); - } - this.container.removeChild(elem); + this.clearRegion(0, 0, this.terminalWidth, this.terminalHeight, + this.color, this.style); }; VT100.prototype.addListener = function(elem, event, listener) { @@ -319,8 +308,17 @@ VT100.prototype.initializeUserCSSStyles = function() { if (++i >= begin) { --c; var label = vt100.usercss.childNodes[j]; - label.innerHTML = - label.innerHTML.replace(/^\u2714 /, ''); + + // Restore label to just the text content + if (typeof label.textContent == 'undefined') { + var s = label.innerText; + label.innerHTML = ''; + label.appendChild(document.createTextNode(s)); + } else { + label.textContent= label.textContent; + } + + // User style sheets are number sequentially var sheet = document.getElementById( 'usercss-' + i); if (i == current) { @@ -330,7 +328,8 @@ VT100.prototype.initializeUserCSSStyles = function() { sheet.disabled = false; } if (!sheet.disabled) { - label.innerHTML= '✔ ' + label.innerHTML; + label.innerHTML= '' + + label.innerHTML; } } else { sheet.disabled = true; @@ -355,7 +354,9 @@ VT100.prototype.initializeUserCSSStyles = function() { // both ends), or whether this is a on/off toggle, which can be grouped // together with other on/off options. group += - '
  • ' + (enabled ? '✔ ' : '') + label + '
  • '; + '
  • ' + (enabled ? '' : '') + + label + + '
  • '; } this.usercss.innerHTML = menu; } @@ -384,8 +385,7 @@ VT100.prototype.initializeElements = function(container) { !this.getChildById(this.container, 'usercss') || !this.getChildById(this.container, 'space') || !this.getChildById(this.container, 'input') || - !this.getChildById(this.container, 'cliphelper') || - !this.getChildById(this.container, 'attrib')) { + !this.getChildById(this.container, 'cliphelper')) { // Only enable the "embed" object, if we have a suitable plugin. Otherwise, // we might get a pointless warning that a suitable plugin is not yet // installed. If in doubt, we'd rather just stay silent. @@ -432,7 +432,6 @@ VT100.prototype.initializeElements = function(container) { '
    ' + '' + '' + - ' ' + (typeof suppressAllAudio != 'undefined' && suppressAllAudio ? "" : embed + '') + @@ -474,7 +473,6 @@ VT100.prototype.initializeElements = function(container) { 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(); @@ -636,12 +634,13 @@ VT100.prototype.repairElements = function(console) { for (var line = console.firstChild; line; line = line.nextSibling) { if (!line.clientHeight) { var newLine = document.createElement(line.tagName); - newLine.style.cssText = line.style.cssText; - newLine.className = line.className; + newLine.style.cssText = line.style.cssText; + newLine.className = line.className; if (line.tagName == 'DIV') { for (var span = line.firstChild; span; span = span.nextSibling) { - var newSpan = document.createElement(span.tagName); - newSpan.style.cssText = span.style.cssText; + var newSpan = document.createElement(span.tagName); + newSpan.style.cssText = span.style.cssText; + newSpan.style.className = span.style.className; this.setTextContent(newSpan, this.getTextContent(span)); newLine.appendChild(newSpan); } @@ -649,7 +648,7 @@ VT100.prototype.repairElements = function(console) { this.setTextContent(newLine, this.getTextContent(line)); } line.parentNode.replaceChild(newLine, line); - line = newLine; + line = newLine; } } }; @@ -1015,7 +1014,7 @@ VT100.prototype.setTextContent = function(elem, s) { } }; -VT100.prototype.insertBlankLine = function(y, style) { +VT100.prototype.insertBlankLine = function(y, color, style) { // Insert a blank line a position y. This method ignores the scrollback // buffer. The caller has to add the length of the scrollback buffer to // the position, if necessary. @@ -1023,22 +1022,26 @@ VT100.prototype.insertBlankLine = function(y, style) { // method just adds a new line right after the last existing one. It does // not add any missing lines in between. It is the caller's responsibility // to do so. - if (style == undefined) { - style = ''; + if (!color) { + color = 'ansi0 bgAnsi15'; + } + if (!style) { + style = ''; } var line; - if (!style) { - line = document.createElement('pre'); + if (color != 'ansi0 bgAnsi15' && !style) { + line = document.createElement('pre'); this.setTextContent(line, '\n'); } else { - line = document.createElement('div'); - var span = document.createElement('span'); - span.style.cssText = style; + line = document.createElement('div'); + var span = document.createElement('span'); + span.style.cssText = style; + span.style.className = color; this.setTextContent(span, this.spaces(this.terminalWidth)); line.appendChild(span); } - line.style.height = this.cursorHeight + 'px'; - var console = this.console[this.currentScreen]; + line.style.height = this.cursorHeight + 'px'; + var console = this.console[this.currentScreen]; if (console.childNodes.length > y) { console.insertBefore(line, console.childNodes[y]); } else { @@ -1104,7 +1107,9 @@ VT100.prototype.truncateLines = function(width) { } // Prune white space from the end of the current line var span = line.lastChild; - while (span && !span.style.cssText.length) { + while (span && + span.className == 'ansi0 bgAnsi15' && + !span.style.cssText.length) { // Scan backwards looking for first non-space character var s = this.getTextContent(span); for (var i = s.length; i--; ) { @@ -1135,7 +1140,10 @@ VT100.prototype.truncateLines = function(width) { } }; -VT100.prototype.putString = function(x, y, text, style) { +VT100.prototype.putString = function(x, y, text, color, style) { + if (!color) { + color = 'ansi0 bgAnsi15'; + } if (!style) { style = ''; } @@ -1192,12 +1200,15 @@ VT100.prototype.putString = function(x, y, text, style) { // If current is not long enough, pad with spaces or add new // span s = this.getTextContent(span); + var oldColor = span.className; var oldStyle = span.style.cssText; if (xPos + s.length < x) { - if (oldStyle != '') { + if (oldColor != 'ansi0 bgAnsi15' || oldStyle != '') { span = document.createElement('span'); line.appendChild(span); + span.className = 'ansi0 bgAnsi15'; span.style.cssText = ''; + oldColor = 'ansi0 bgAnsi15'; oldStyle = ''; xPos += s.length; s = ''; @@ -1209,7 +1220,8 @@ VT100.prototype.putString = function(x, y, text, style) { // If styles do not match, create a new var del = text.length - s.length + x - xPos; - if (oldStyle != style && (oldStyle || style)) { + if (oldColor != color || + (oldStyle != style && (oldStyle || style))) { if (xPos == x) { // Replacing text at beginning of existing if (text.length >= s.length) { @@ -1236,6 +1248,7 @@ VT100.prototype.putString = function(x, y, text, style) { span = sibling; if (remainder.length) { sibling = document.createElement('span'); + sibling.className = oldColor; sibling.style.cssText = oldStyle; this.setTextContent(sibling, remainder); line.insertBefore(sibling, span.nextSibling); @@ -1245,6 +1258,7 @@ VT100.prototype.putString = function(x, y, text, style) { span = sibling; if (remainder.length) { sibling = document.createElement('span'); + sibling.className = oldColor; sibling.style.cssText = oldStyle; this.setTextContent(sibling, remainder); line.appendChild(sibling); @@ -1252,6 +1266,7 @@ VT100.prototype.putString = function(x, y, text, style) { } s = text; } + span.className = color; span.style.cssText = style; } else { // Overwrite (partial) with new text @@ -1278,7 +1293,8 @@ VT100.prototype.putString = function(x, y, text, style) { } // Merge with next sibling, if styles are identical - if (sibling && span.style.cssText == sibling.style.cssText) { + if (sibling && span.className == sibling.className && + span.style.cssText == sibling.style.cssText) { this.setTextContent(span, this.getTextContent(span) + this.getTextContent(sibling)); @@ -1348,6 +1364,7 @@ VT100.prototype.putString = function(x, y, text, style) { if (text.length) { // Merge with previous sibling, if styles are identical if ((sibling = span.previousSibling) && + span.className == sibling.className && span.style.cssText == sibling.style.cssText) { this.setTextContent(span, this.getTextContent(sibling) + @@ -1357,7 +1374,9 @@ VT100.prototype.putString = function(x, y, text, style) { // Prune white space from the end of the current line span = line.lastChild; - while (span && !span.style.cssText.length) { + while (span && + span.className == 'ansi0 bgAnsi15' && + !span.style.cssText.length) { // Scan backwards looking for first non-space character s = this.getTextContent(span); for (var i = s.length; i--; ) { @@ -1418,11 +1437,10 @@ VT100.prototype.gotoXaY = function(x, y) { VT100.prototype.refreshInvertedState = function() { if (this.isInverted) { - this.scrollable.style.color = this.ansi[15]; - this.scrollable.style.backgroundColor = this.ansi[0]; + this.scrollable.className += ' inverted'; } else { - this.scrollable.style.color = ''; - this.scrollable.style.backgroundColor = ''; + this.scrollable.className = this.scrollable.className. + replace(/ *inverted/, ''); } }; @@ -1502,7 +1520,7 @@ VT100.prototype.spaces = function(i) { return s; }; -VT100.prototype.clearRegion = function(x, y, w, h, style) { +VT100.prototype.clearRegion = function(x, y, w, h, color, style) { w += x; if (x < 0) { x = 0; @@ -1529,7 +1547,7 @@ VT100.prototype.clearRegion = function(x, y, w, h, style) { // child nodes. if (!this.numScrollbackLines && w == this.terminalWidth && h == this.terminalHeight && - !style) { + (color == undefined || color == 'ansi0 bgAnsi15') && !style) { var console = this.console[this.currentScreen]; while (console.lastChild) { console.removeChild(console.lastChild); @@ -1541,54 +1559,66 @@ VT100.prototype.clearRegion = function(x, y, w, h, style) { var cy = this.cursorY; var s = this.spaces(w); for (var i = y+h; i-- > y; ) { - this.putString(x, i, s, style); + this.putString(x, i, s, color, style); } hidden ? this.showCursor(cx, cy) : this.putString(cx, cy, '', undefined); } }; VT100.prototype.copyLineSegment = function(dX, dY, sX, sY, w) { - var text = [ ]; - var style = [ ]; - var console = this.console[this.currentScreen]; + var text = [ ]; + var className = [ ]; + var style = [ ]; + var console = this.console[this.currentScreen]; if (sY >= console.childNodes.length) { - text[0] = this.spaces(w); - style[0] = null; + text[0] = this.spaces(w); + className[0] = undefined; + style[0] = undefined; } else { var line = console.childNodes[sY]; if (line.tagName != 'DIV' || !line.childNodes.length) { - text[0] = this.spaces(w); - style[0] = null; + text[0] = this.spaces(w); + className[0] = undefined; + style[0] = undefined; } else { - var x = 0; + var x = 0; for (var span = line.firstChild; span && w > 0; span = span.nextSibling){ - var s = this.getTextContent(span); - var len = s.length; + var s = this.getTextContent(span); + var len = s.length; if (x + len > sX) { - var o = sX > x ? sX - x : 0; - text[text.length] = s.substr(o, w); - style[style.length] = span.style.cssText; - w -= len - o; + var o = sX > x ? sX - x : 0; + text[text.length] = s.substr(o, w); + className[className.length] = span.className; + style[style.length] = span.style.cssText; + w -= len - o; } - x += len; + x += len; } if (w > 0) { - text[text.length] = this.spaces(w); - style[style.length] = null; + text[text.length] = this.spaces(w); + className[className.length] = undefined; + style[style.length] = undefined; } } } - var hidden = this.hideCursor(); - var cx = this.cursorX; - var cy = this.cursorY; + var hidden = this.hideCursor(); + var cx = this.cursorX; + var cy = this.cursorY; for (var i = 0; i < text.length; i++) { - this.putString(dX, dY - this.numScrollbackLines, text[i], style[i]); - dX += text[i].length; + var color; + if (className[i]) { + color = className[i]; + } else { + color = 'ansi0 bgAnsi15'; + } + this.putString(dX, dY - this.numScrollbackLines, text[i], color, style[i]); + dX += text[i].length; } hidden ? this.showCursor(cx, cy) : this.putString(cx, cy, '', undefined); }; -VT100.prototype.scrollRegion = function(x, y, w, h, incX, incY, style) { +VT100.prototype.scrollRegion = function(x, y, w, h, incX, incY, + color, style) { var left = incX < 0 ? -incX : 0; var right = incX > 0 ? incX : 0; var up = incY < 0 ? -incY : 0; @@ -1623,9 +1653,7 @@ VT100.prototype.scrollRegion = function(x, y, w, h, incX, incY, style) { // fill with underlined spaces. N.B. this is different from the // cases when the user blanks a region. User-initiated blanking // always fills with all of the current attributes. - this.attributeHelper.cssText - = style.replace(/text-decoration:underline;/, ""); - style = this.attributeHelper.cssText; + style = style.replace(/text-decoration:underline;/, ''); } // Compute current scroll position @@ -1654,7 +1682,7 @@ VT100.prototype.scrollRegion = function(x, y, w, h, incX, incY, style) { // Add new lines at bottom in order to force scrolling for (var i = 0; i < y; i++) { - this.insertBlankLine(console.childNodes.length, style); + this.insertBlankLine(console.childNodes.length, color, style); } // Adjust the number of lines in the scrollback buffer by @@ -1691,7 +1719,7 @@ VT100.prototype.scrollRegion = function(x, y, w, h, incX, incY, style) { console.childNodes.length > this.numScrollbackLines+y+h+incY) { for (var i = -incY; i-- > 0; ) { this.insertBlankLine(this.numScrollbackLines + y + h + incY, - style); + color, style); } } } @@ -1703,7 +1731,7 @@ VT100.prototype.scrollRegion = function(x, y, w, h, incX, incY, style) { console.removeChild(console.childNodes[this.numScrollbackLines+y+h]); } for (var i = incY; i--; ) { - this.insertBlankLine(this.numScrollbackLines + y, style); + this.insertBlankLine(this.numScrollbackLines + y, color, style); } } } else { @@ -1725,14 +1753,14 @@ VT100.prototype.scrollRegion = function(x, y, w, h, incX, incY, style) { // Clear blank regions if (incX > 0) { - this.clearRegion(x, y, incX, h, style); + this.clearRegion(x, y, incX, h, color, style); } else if (incX < 0) { - this.clearRegion(x + w + incX, y, -incX, h, style); + this.clearRegion(x + w + incX, y, -incX, h, color, style); } if (incY > 0) { - this.clearRegion(x, y, w, incY, style); + this.clearRegion(x, y, w, incY, color, style); } else if (incY < 0) { - this.clearRegion(x, y + h + incY, w, -incY, style); + this.clearRegion(x, y + h + incY, w, -incY, color, style); } } @@ -1829,9 +1857,11 @@ VT100.prototype.showContextMenu = function(x, y) { '
  • Reset
  • ' + '
    ' + '
  • ' + - (this.utfEnabled ? '✔ ' : '') + 'Unicode
  • ' + + (this.utfEnabled ? '' : '') + + 'Unicode' + '
  • ' + - (this.visualBell ? '✔ ' : '') + 'Visual Bell
  • '+ + (this.visualBell ? '' : '') + + 'Visual Bell'+ (this.usercss.firstChild ? '
    ' + this.usercss.innerHTML + @@ -2576,7 +2606,7 @@ VT100.prototype.lf = function(count) { if (this.cursorY == this.bottom - 1) { this.scrollRegion(0, this.top + 1, this.terminalWidth, this.bottom - this.top - 1, - 0, -1, this.style); + 0, -1, this.color, this.style); offset = undefined; } else if (this.cursorY < this.terminalHeight - 1) { this.gotoXY(this.cursorX, this.cursorY + 1); @@ -2599,7 +2629,7 @@ VT100.prototype.ri = function(count) { if (this.cursorY == this.top) { this.scrollRegion(0, this.top, this.terminalWidth, this.bottom - this.top - 1, - 0, 1, this.style); + 0, 1, this.color, this.style); } else if (this.cursorY > 0) { this.gotoXY(this.cursorX, this.cursorY - 1); } @@ -2615,48 +2645,42 @@ VT100.prototype.respondSecondaryDA = function() { this.respondString += '\u001B[>0;0;0c'; }; + VT100.prototype.updateStyle = function() { - var style = ''; + this.style = ''; if (this.attr & ATTR_UNDERLINE) { - style += 'text-decoration:underline;'; + this.style = 'text-decoration:underline;'; } - var bg = (this.attr >> 4) & 0xF; - var fg = this.attr & 0xF; + var bg = (this.attr >> 4) & 0xF; + var fg = this.attr & 0xF; if (this.attr & ATTR_REVERSE) { - var tmp = bg; - bg = fg; - fg = tmp; + var tmp = bg; + bg = fg; + fg = tmp; } if ((this.attr & (ATTR_REVERSE | ATTR_DIM)) == ATTR_DIM) { - fg = 8; // Dark grey + fg = 8; // Dark grey } else if (this.attr & ATTR_BRIGHT) { - fg |= 8; + fg |= 8; } if (this.attr & ATTR_BLINK) { - bg ^= 8; + bg ^= 8; } // Make some readability enhancements. Most notably, disallow identical // background and foreground colors. if (bg == fg) { - if ((fg ^= 8) == 7) { - fg = 8; + if ((fg ^= 8) == 7) { + fg = 8; } } // And disallow bright colors on a light-grey background. if (bg == 7 && fg >= 8) { - if ((fg -= 8) == 7) { - fg = 8; + if ((fg -= 8) == 7) { + fg = 8; } } - if (fg != 0) { - style += 'color:' + this.ansi[fg] + ';'; - } - if (bg != 15) { - style += 'background-color:' + this.ansi[bg] + ';'; - } - this.attributeHelper.cssText = style; - this.style = this.attributeHelper.cssText; + this.color = 'ansi' + fg + ' bgAnsi' + bg; }; VT100.prototype.setAttrColors = function(attr) { @@ -2750,7 +2774,7 @@ VT100.prototype.csiAt = function(number) { } this.scrollRegion(this.cursorX, this.cursorY, this.terminalWidth - this.cursorX - number, 1, - number, 0, this.style); + number, 0, this.color, this.style); this.needWrap = false; }; @@ -2758,22 +2782,26 @@ VT100.prototype.csiJ = function(number) { switch (number) { case 0: // Erase from cursor to end of display this.clearRegion(this.cursorX, this.cursorY, - this.terminalWidth - this.cursorX, 1, this.style); + this.terminalWidth - this.cursorX, 1, + this.color, this.style); if (this.cursorY < this.terminalHeight-2) { this.clearRegion(0, this.cursorY+1, this.terminalWidth, this.terminalHeight-this.cursorY-1, - this.style); + this.color, this.style); } break; case 1: // Erase from start to cursor if (this.cursorY > 0) { this.clearRegion(0, 0, - this.terminalWidth, this.cursorY, this.style); + this.terminalWidth, this.cursorY, + this.color, this.style); } - this.clearRegion(0, this.cursorY, this.cursorX + 1, 1, this.style); + this.clearRegion(0, this.cursorY, this.cursorX + 1, 1, + this.color, this.style); break; case 2: // Erase whole display - this.clearRegion(0, 0, this.terminalWidth, this.terminalHeight,this.style); + this.clearRegion(0, 0, this.terminalWidth, this.terminalHeight, + this.color, this.style); break; default: return; @@ -2785,13 +2813,16 @@ VT100.prototype.csiK = function(number) { switch (number) { case 0: // Erase from cursor to end of line this.clearRegion(this.cursorX, this.cursorY, - this.terminalWidth - this.cursorX, 1, this.style); + this.terminalWidth - this.cursorX, 1, + this.color, this.style); break; case 1: // Erase from start of line to cursor - this.clearRegion(0, this.cursorY, this.cursorX + 1, 1, this.style); + this.clearRegion(0, this.cursorY, this.cursorX + 1, 1, + this.color, this.style); break; case 2: // Erase whole line - this.clearRegion(0, this.cursorY, this.terminalWidth, 1, this.style); + this.clearRegion(0, this.cursorY, this.terminalWidth, 1, + this.color, this.style); break; default: return; @@ -2812,7 +2843,7 @@ VT100.prototype.csiL = function(number) { } this.scrollRegion(0, this.cursorY, this.terminalWidth, this.bottom - this.cursorY - number, - 0, number, this.style); + 0, number, this.color, this.style); needWrap = false; }; @@ -2829,7 +2860,7 @@ VT100.prototype.csiM = function(number) { } this.scrollRegion(0, this.cursorY + number, this.terminalWidth, this.bottom - this.cursorY - number, - 0, -number, this.style); + 0, -number, this.color, this.style); needWrap = false; }; @@ -2890,7 +2921,7 @@ VT100.prototype.csiP = function(number) { } this.scrollRegion(this.cursorX + number, this.cursorY, this.terminalWidth - this.cursorX - number, 1, - -number, 0, this.style); + -number, 0, this.color, this.style); needWrap = false; }; @@ -2902,7 +2933,8 @@ VT100.prototype.csiX = function(number) { if (number > this.terminalWidth - this.cursorX) { number = this.terminalWidth - this.cursorX; } - this.clearRegion(this.cursorX, this.cursorY, number, 1, this.style); + this.clearRegion(this.cursorX, this.cursorY, number, 1, + this.color, this.style); needWrap = false; }; @@ -3224,7 +3256,7 @@ VT100.prototype.renderString = function(s, showCursor) { // call to this.showCursor() this.cursor.style.visibility = ''; } - this.putString(this.cursorX, this.cursorY, s, this.style); + this.putString(this.cursorX, this.cursorY, s, this.color, this.style); }; VT100.prototype.vt100 = function(s) { @@ -3299,7 +3331,7 @@ VT100.prototype.vt100 = function(s) { if (this.insertMode) { this.scrollRegion(this.cursorX, this.cursorY, this.terminalWidth - this.cursorX - 1, 1, - 1, 0, this.style); + 1, 0, this.color, this.style); } this.lastCharacter = String.fromCharCode(ch); lineBuf += this.lastCharacter; diff --git a/shellinabox/white-on-black.css b/shellinabox/white-on-black.css index db2dcf8..c64d5b1 100644 --- a/shellinabox/white-on-black.css +++ b/shellinabox/white-on-black.css @@ -1,6 +1,6 @@ -#vt100 #scrollable { - color: white; - background-color: black; -} -#vt100 #ansi0 { background-color: #ffffff; } -#vt100 #ansi15 { background-color: #000000; } +#vt100 #scrollable { color: #ffffff; + background-color: #000000; } +#vt100 #scrollable.inverted { color: #000000; + background-color: #ffffff; } +#vt100 .ansi15 { color: #000000; } +#vt100 .bgAnsi0 { background-color: #ffffff; }