Added an optional on-screen keyboard. Must be activated by the user by selecting the option in the context-menu.

git-svn-id: https://shellinabox.googlecode.com/svn/trunk@221 0da03de8-d603-11dd-86c2-0f8696b7b6f9
This commit is contained in:
zodiac@gmail.com 2010-09-04 17:50:11 +00:00
parent 13d0448fc6
commit 2c262e1077
16 changed files with 2026 additions and 481 deletions

View file

@ -1,3 +1,8 @@
2010-09-04 Markus Gutschke <markus@shellinabox.com>
* Added an optional on-screen keyboard. Must be activated by the
user by selecting the option in the context-menu.
2010-09-03 Markus Gutschke <markus@shellinabox.com> 2010-09-03 Markus Gutschke <markus@shellinabox.com>
* Fix some scaling related issues. This fix is thanks to some * Fix some scaling related issues. This fix is thanks to some

View file

@ -32,6 +32,7 @@ EXTRA_DIST = INSTALL.Debian \
demo/demo.jspp \ demo/demo.jspp \
demo/demo.xml \ demo/demo.xml \
demo/enabled.gif \ demo/enabled.gif \
demo/keyboard.png \
demo/styles.css \ demo/styles.css \
demo/print-styles.css \ demo/print-styles.css \
demo/vt100.js \ demo/vt100.js \
@ -106,6 +107,8 @@ shellinaboxd_SOURCES = shellinabox/shellinaboxd.c \
shellinabox/print-styles.css \ shellinabox/print-styles.css \
shellinabox/enabled.gif \ shellinabox/enabled.gif \
shellinabox/favicon.ico \ shellinabox/favicon.ico \
shellinabox/keyboard.png \
shellinabox/keyboard-layout.html \
shellinabox/beep.wav \ shellinabox/beep.wav \
config.h config.h
shellinaboxd_LDADD = liblogging.la \ shellinaboxd_LDADD = liblogging.la \
@ -148,7 +151,9 @@ libtool: $(LIBTOOL_DEPS)
${top_srcdir}/demo/demo.js: ${top_srcdir}/demo/beep.wav \ ${top_srcdir}/demo/demo.js: ${top_srcdir}/demo/beep.wav \
${top_srcdir}/demo/demo.jspp \ ${top_srcdir}/demo/demo.jspp \
${top_srcdir}/demo/enabled.gif \
${top_srcdir}/demo/favicon.ico \ ${top_srcdir}/demo/favicon.ico \
${top_srcdir}/demo/keyboard.png \
${top_srcdir}/demo/styles.css \ ${top_srcdir}/demo/styles.css \
${top_srcdir}/demo/print-styles.css \ ${top_srcdir}/demo/print-styles.css \
${top_srcdir}/demo/vt100.js \ ${top_srcdir}/demo/vt100.js \
@ -169,6 +174,10 @@ ${top_srcdir}/demo/favicon.ico: ${top_srcdir}/shellinabox/favicon.ico
@rm -f "$@" @rm -f "$@"
ln "$?" "$@" ln "$?" "$@"
${top_srcdir}/demo/keyboard.png: ${top_srcdir}/shellinabox/keyboard.png
@rm -f "$@"
ln "$?" "$@"
${top_srcdir}/demo/styles.css: ${top_srcdir}/shellinabox/styles.css ${top_srcdir}/demo/styles.css: ${top_srcdir}/shellinabox/styles.css
@rm -f "$@" @rm -f "$@"
sed -e '/\[if DEFINES_COLORS\]/,/\[endif DEFINES_COLORS\]/d' "$?" >"$@" sed -e '/\[if DEFINES_COLORS\]/,/\[endif DEFINES_COLORS\]/d' "$?" >"$@"
@ -197,7 +206,8 @@ ${top_srcdir}/demo/vt100.js: ${top_srcdir}/shellinabox/vt100.js
@rm -f "$@" @rm -f "$@"
ln "$?" "$@" ln "$?" "$@"
shellinaboxd.1: shellinabox/shellinaboxd.man.in config.h shellinaboxd.1: ${top_srcdir}/shellinabox/shellinaboxd.man.in \
${top_srcdir}/config.h
@src="${top_srcdir}/shellinabox/shellinaboxd.man.in"; \ @src="${top_srcdir}/shellinabox/shellinaboxd.man.in"; \
echo preprocess "$$src" '>'"$@"; \ echo preprocess "$$src" '>'"$@"; \
if sed -e 's/^#define \([^ ]*\).*/\1/' -e t -e d config.h | \ if sed -e 's/^#define \([^ ]*\).*/\1/' -e t -e d config.h | \
@ -253,6 +263,13 @@ clean-local:
$(OBJCOPY) --add-section .note.GNU-stack=GNU-stack "$@"; \ $(OBJCOPY) --add-section .note.GNU-stack=GNU-stack "$@"; \
rm -f GNU-stack rm -f GNU-stack
.png.o:
@echo $(OBJCOPY) "$<" "$@"
@$(OBJCOPY) -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)`\
"$<" "$@"
@-printf '\000' >GNU-stack && \
$(OBJCOPY) --add-section .note.GNU-stack=GNU-stack "$@"; \
rm -f GNU-stack
.html.o: .html.o:
@echo $(OBJCOPY) "$<" "$@" @echo $(OBJCOPY) "$<" "$@"
@ -272,15 +289,23 @@ clean-local:
rm -f GNU-stack rm -f GNU-stack
shellinabox/shell_in_a_box.o: shellinabox/shell_in_a_box.js config.h shellinabox/shell_in_a_box.o: ${top_srcdir}/shellinabox/shell_in_a_box.js \
${top_srcdir}/config.h
${top_srcdir}/shellinabox/vt100.js: ${top_srcdir}/shellinabox/vt100.jspp \
${top_srcdir}/shellinabox/keyboard-layout.html
.jspp.js: .jspp.js:
@echo preprocess "$<" "$@" @echo preprocess "$<" "$@"
@sed -e "`sed -e 's/^#define *\([^ ]*\) *\(.*\)/\/^[^#]\/s\/\1\/\2 \\\\\/* \1 *\\\\\/\/g/' \ @kbd=`while read i; do \
printf '%s' "\`echo "$$i" | sed 's/&/\\\\\\&/g'\`"; \
done <${top_srcdir}/shellinabox/keyboard-layout.html`; \
sed -e "`sed -e 's/^#define *\([^ ]*\) *\(.*\)/\/^[^#]\/s\/\1\/\2 \\\\\/* \1 *\\\\\/\/g/' \
-e t \ -e t \
-e d "$<"`" \ -e d "$<"`" \
-e "s/^#/\/\/ #/" \ -e "s/^#/\/\/ #/" \
-e "s/VERSION/\"@VERSION@ (revision @VCS_REVISION@)\"/g" \ -e "s/VERSION/\"@VERSION@ (revision @VCS_REVISION@)\"/g" \
-e "s%KEYBOARD%'$${kbd}'%" \
"$<" >"$@" "$<" >"$@"
.js.o: .js.o:

View file

@ -82,6 +82,8 @@ am_shellinaboxd_OBJECTS = shellinaboxd.$(OBJEXT) \
shellinabox/styles.$(OBJEXT) \ shellinabox/styles.$(OBJEXT) \
shellinabox/print-styles.$(OBJEXT) \ shellinabox/print-styles.$(OBJEXT) \
shellinabox/enabled.$(OBJEXT) shellinabox/favicon.$(OBJEXT) \ shellinabox/enabled.$(OBJEXT) shellinabox/favicon.$(OBJEXT) \
shellinabox/keyboard.$(OBJEXT) \
shellinabox/keyboard-layout.$(OBJEXT) \
shellinabox/beep.$(OBJEXT) shellinabox/beep.$(OBJEXT)
shellinaboxd_OBJECTS = $(am_shellinaboxd_OBJECTS) shellinaboxd_OBJECTS = $(am_shellinaboxd_OBJECTS)
shellinaboxd_DEPENDENCIES = liblogging.la libhttp.la shellinaboxd_DEPENDENCIES = liblogging.la libhttp.la
@ -288,6 +290,7 @@ EXTRA_DIST = INSTALL.Debian \
demo/demo.jspp \ demo/demo.jspp \
demo/demo.xml \ demo/demo.xml \
demo/enabled.gif \ demo/enabled.gif \
demo/keyboard.png \
demo/styles.css \ demo/styles.css \
demo/print-styles.css \ demo/print-styles.css \
demo/vt100.js \ demo/vt100.js \
@ -366,6 +369,8 @@ shellinaboxd_SOURCES = shellinabox/shellinaboxd.c \
shellinabox/print-styles.css \ shellinabox/print-styles.css \
shellinabox/enabled.gif \ shellinabox/enabled.gif \
shellinabox/favicon.ico \ shellinabox/favicon.ico \
shellinabox/keyboard.png \
shellinabox/keyboard-layout.html \
shellinabox/beep.wav \ shellinabox/beep.wav \
config.h config.h
@ -407,7 +412,7 @@ all: config.h
$(MAKE) $(AM_MAKEFLAGS) all-am $(MAKE) $(AM_MAKEFLAGS) all-am
.SUFFIXES: .SUFFIXES:
.SUFFIXES: .c .css .gif .html .ico .js .jspp .lo .o .obj .wav .SUFFIXES: .c .css .gif .html .ico .js .jspp .lo .o .obj .png .wav
am--refresh: am--refresh:
@: @:
$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
@ -537,6 +542,10 @@ shellinabox/enabled.$(OBJEXT): shellinabox/$(am__dirstamp) \
shellinabox/$(DEPDIR)/$(am__dirstamp) shellinabox/$(DEPDIR)/$(am__dirstamp)
shellinabox/favicon.$(OBJEXT): shellinabox/$(am__dirstamp) \ shellinabox/favicon.$(OBJEXT): shellinabox/$(am__dirstamp) \
shellinabox/$(DEPDIR)/$(am__dirstamp) shellinabox/$(DEPDIR)/$(am__dirstamp)
shellinabox/keyboard.$(OBJEXT): shellinabox/$(am__dirstamp) \
shellinabox/$(DEPDIR)/$(am__dirstamp)
shellinabox/keyboard-layout.$(OBJEXT): shellinabox/$(am__dirstamp) \
shellinabox/$(DEPDIR)/$(am__dirstamp)
shellinabox/beep.$(OBJEXT): shellinabox/$(am__dirstamp) \ shellinabox/beep.$(OBJEXT): shellinabox/$(am__dirstamp) \
shellinabox/$(DEPDIR)/$(am__dirstamp) shellinabox/$(DEPDIR)/$(am__dirstamp)
shellinaboxd$(EXEEXT): $(shellinaboxd_OBJECTS) $(shellinaboxd_DEPENDENCIES) shellinaboxd$(EXEEXT): $(shellinaboxd_OBJECTS) $(shellinaboxd_DEPENDENCIES)
@ -549,6 +558,8 @@ mostlyclean-compile:
-rm -f shellinabox/cgi_root.$(OBJEXT) -rm -f shellinabox/cgi_root.$(OBJEXT)
-rm -f shellinabox/enabled.$(OBJEXT) -rm -f shellinabox/enabled.$(OBJEXT)
-rm -f shellinabox/favicon.$(OBJEXT) -rm -f shellinabox/favicon.$(OBJEXT)
-rm -f shellinabox/keyboard-layout.$(OBJEXT)
-rm -f shellinabox/keyboard.$(OBJEXT)
-rm -f shellinabox/print-styles.$(OBJEXT) -rm -f shellinabox/print-styles.$(OBJEXT)
-rm -f shellinabox/root_page.$(OBJEXT) -rm -f shellinabox/root_page.$(OBJEXT)
-rm -f shellinabox/shell_in_a_box.$(OBJEXT) -rm -f shellinabox/shell_in_a_box.$(OBJEXT)
@ -1162,7 +1173,9 @@ libtool: $(LIBTOOL_DEPS)
${top_srcdir}/demo/demo.js: ${top_srcdir}/demo/beep.wav \ ${top_srcdir}/demo/demo.js: ${top_srcdir}/demo/beep.wav \
${top_srcdir}/demo/demo.jspp \ ${top_srcdir}/demo/demo.jspp \
${top_srcdir}/demo/enabled.gif \
${top_srcdir}/demo/favicon.ico \ ${top_srcdir}/demo/favicon.ico \
${top_srcdir}/demo/keyboard.png \
${top_srcdir}/demo/styles.css \ ${top_srcdir}/demo/styles.css \
${top_srcdir}/demo/print-styles.css \ ${top_srcdir}/demo/print-styles.css \
${top_srcdir}/demo/vt100.js \ ${top_srcdir}/demo/vt100.js \
@ -1183,6 +1196,10 @@ ${top_srcdir}/demo/favicon.ico: ${top_srcdir}/shellinabox/favicon.ico
@rm -f "$@" @rm -f "$@"
ln "$?" "$@" ln "$?" "$@"
${top_srcdir}/demo/keyboard.png: ${top_srcdir}/shellinabox/keyboard.png
@rm -f "$@"
ln "$?" "$@"
${top_srcdir}/demo/styles.css: ${top_srcdir}/shellinabox/styles.css ${top_srcdir}/demo/styles.css: ${top_srcdir}/shellinabox/styles.css
@rm -f "$@" @rm -f "$@"
sed -e '/\[if DEFINES_COLORS\]/,/\[endif DEFINES_COLORS\]/d' "$?" >"$@" sed -e '/\[if DEFINES_COLORS\]/,/\[endif DEFINES_COLORS\]/d' "$?" >"$@"
@ -1211,7 +1228,8 @@ ${top_srcdir}/demo/vt100.js: ${top_srcdir}/shellinabox/vt100.js
@rm -f "$@" @rm -f "$@"
ln "$?" "$@" ln "$?" "$@"
shellinaboxd.1: shellinabox/shellinaboxd.man.in config.h shellinaboxd.1: ${top_srcdir}/shellinabox/shellinaboxd.man.in \
${top_srcdir}/config.h
@src="${top_srcdir}/shellinabox/shellinaboxd.man.in"; \ @src="${top_srcdir}/shellinabox/shellinaboxd.man.in"; \
echo preprocess "$$src" '>'"$@"; \ echo preprocess "$$src" '>'"$@"; \
if sed -e 's/^#define \([^ ]*\).*/\1/' -e t -e d config.h | \ if sed -e 's/^#define \([^ ]*\).*/\1/' -e t -e d config.h | \
@ -1267,6 +1285,14 @@ clean-local:
$(OBJCOPY) --add-section .note.GNU-stack=GNU-stack "$@"; \ $(OBJCOPY) --add-section .note.GNU-stack=GNU-stack "$@"; \
rm -f GNU-stack rm -f GNU-stack
.png.o:
@echo $(OBJCOPY) "$<" "$@"
@$(OBJCOPY) -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)`\
"$<" "$@"
@-printf '\000' >GNU-stack && \
$(OBJCOPY) --add-section .note.GNU-stack=GNU-stack "$@"; \
rm -f GNU-stack
.html.o: .html.o:
@echo $(OBJCOPY) "$<" "$@" @echo $(OBJCOPY) "$<" "$@"
@$(OBJCOPY) -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)`\ @$(OBJCOPY) -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)`\
@ -1283,15 +1309,23 @@ clean-local:
$(OBJCOPY) --add-section .note.GNU-stack=GNU-stack "$@"; \ $(OBJCOPY) --add-section .note.GNU-stack=GNU-stack "$@"; \
rm -f GNU-stack rm -f GNU-stack
shellinabox/shell_in_a_box.o: shellinabox/shell_in_a_box.js config.h shellinabox/shell_in_a_box.o: ${top_srcdir}/shellinabox/shell_in_a_box.js \
${top_srcdir}/config.h
${top_srcdir}/shellinabox/vt100.js: ${top_srcdir}/shellinabox/vt100.jspp \
${top_srcdir}/shellinabox/keyboard-layout.html
.jspp.js: .jspp.js:
@echo preprocess "$<" "$@" @echo preprocess "$<" "$@"
@sed -e "`sed -e 's/^#define *\([^ ]*\) *\(.*\)/\/^[^#]\/s\/\1\/\2 \\\\\/* \1 *\\\\\/\/g/' \ @kbd=`while read i; do \
printf '%s' "\`echo "$$i" | sed 's/&/\\\\\\&/g'\`"; \
done <${top_srcdir}/shellinabox/keyboard-layout.html`; \
sed -e "`sed -e 's/^#define *\([^ ]*\) *\(.*\)/\/^[^#]\/s\/\1\/\2 \\\\\/* \1 *\\\\\/\/g/' \
-e t \ -e t \
-e d "$<"`" \ -e d "$<"`" \
-e "s/^#/\/\/ #/" \ -e "s/^#/\/\/ #/" \
-e "s/VERSION/\"@VERSION@ (revision @VCS_REVISION@)\"/g" \ -e "s/VERSION/\"@VERSION@ (revision @VCS_REVISION@)\"/g" \
-e "s%KEYBOARD%'$${kbd}'%" \
"$<" >"$@" "$<" >"$@"
.js.o: .js.o:

View file

@ -153,7 +153,7 @@
#define STDC_HEADERS 1 #define STDC_HEADERS 1
/* Most recent revision number in the version control system */ /* Most recent revision number in the version control system */
#define VCS_REVISION "220" #define VCS_REVISION "221"
/* Version number of package */ /* Version number of package */
#define VERSION "2.10" #define VERSION "2.10"

2
configure vendored
View file

@ -2328,7 +2328,7 @@ ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $
ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_compiler_gnu=$ac_cv_c_compiler_gnu
VCS_REVISION=220 VCS_REVISION=221
cat >>confdefs.h <<_ACEOF cat >>confdefs.h <<_ACEOF

View file

@ -2,7 +2,7 @@ AC_PREREQ(2.57)
dnl This is the one location where the authoritative version number is stored dnl This is the one location where the authoritative version number is stored
AC_INIT(shellinabox, 2.10, markus@shellinabox.com) AC_INIT(shellinabox, 2.10, markus@shellinabox.com)
VCS_REVISION=220 VCS_REVISION=221
AC_SUBST(VCS_REVISION) AC_SUBST(VCS_REVISION)
AC_DEFINE_UNQUOTED(VCS_REVISION, "${VCS_REVISION}", AC_DEFINE_UNQUOTED(VCS_REVISION, "${VCS_REVISION}",
[Most recent revision number in the version control system]) [Most recent revision number in the version control system])

BIN
demo/keyboard.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 808 B

View file

@ -1,156 +1,233 @@
#vt100 a { #vt100 a {
text-decoration: none; text-decoration: none;
color: inherit; color: inherit;
} }
#vt100 a:hover { #vt100 a:hover {
text-decoration: underline; text-decoration: underline;
} }
#vt100 #reconnect { #vt100 #reconnect {
position: absolute; position: absolute;
z-index: 2; z-index: 2;
} }
#vt100 #reconnect input { #vt100 #reconnect input {
padding: 1ex; padding: 1ex;
font-weight: bold; font-weight: bold;
font-size: x-large; font-size: x-large;
} }
#vt100 #cursize { #vt100 #cursize {
background: #EEEEEE; background: #EEEEEE;
border: 1px solid black; border: 1px solid black;
font-family: sans-serif; font-family: sans-serif;
font-size: large; font-size: large;
font-weight: bold; font-weight: bold;
padding: 1ex; padding: 1ex;
position: absolute; position: absolute;
z-index: 2; z-index: 2;
} }
#vt100 pre { #vt100 pre {
margin: 0px; margin: 0px;
} }
#vt100 pre pre { #vt100 pre pre {
overflow: hidden; overflow: hidden;
} }
#vt100 #scrollable { #vt100 #scrollable {
overflow-x: hidden; overflow-x: hidden;
overflow-y: scroll; overflow-y: scroll;
position: relative; position: relative;
padding: 1px; padding: 1px;
} }
#vt100 #console, #vt100 #alt_console, #vt100 #cursor, #vt100 #lineheight, #vt100 .hidden pre { #vt100 #console, #vt100 #alt_console, #vt100 #cursor, #vt100 #lineheight, #vt100 .hidden pre {
font-family: "DejaVu Sans Mono", "Everson Mono", FreeMono, "Andale Mono", "Lucida Console", monospace; font-family: "DejaVu Sans Mono", "Everson Mono", FreeMono, "Andale Mono", "Lucida Console", monospace;
} }
#vt100 #lineheight { #vt100 #lineheight {
position: absolute; position: absolute;
visibility: hidden; visibility: hidden;
} }
#vt100 #cursor { #vt100 #cursor {
position: absolute; position: absolute;
left: 0px; left: 0px;
top: 0px; top: 0px;
overflow: hidden; overflow: hidden;
z-index: 1; z-index: 1;
} }
#vt100 #cursor.bright { #vt100 #cursor.bright {
background-color: #e60000; background-color: #e60000;
color: white; color: white;
} }
#vt100 #cursor.dim { #vt100 #cursor.dim {
visibility: hidden; visibility: hidden;
} }
#vt100 #cursor.inactive { #vt100 #cursor.inactive {
border: 1px solid #e60000; border: 1px solid #e60000;
margin: -1px; margin: -1px;
} }
#vt100 #padding { #vt100 #padding {
visibility: hidden; visibility: hidden;
width: 1px; width: 1px;
height: 0px; height: 0px;
overflow: hidden; overflow: hidden;
} }
#vt100 .hidden { #vt100 .hidden {
position: absolute; position: absolute;
top: -10000px; top: -10000px;
left: -10000px; left: -10000px;
width: 0px; width: 0px;
height: 0px; height: 0px;
} }
#vt100 #menu { #vt100 #menu {
overflow: visible; overflow: visible;
position: absolute; position: absolute;
z-index: 3; z-index: 3;
} }
#vt100 #menu .popup { #vt100 #menu .popup {
background-color: #EEEEEE; background-color: #EEEEEE;
border: 1px solid black; border: 1px solid black;
font-family: sans-serif; font-family: sans-serif;
position: absolute; position: absolute;
} }
#vt100 #menu .popup ul { #vt100 #menu .popup ul {
list-style-type: none; list-style-type: none;
padding: 0px; padding: 0px;
margin: 0px; margin: 0px;
min-width: 10em; min-width: 10em;
} }
#vt100 #menu .popup li { #vt100 #menu .popup li {
padding: 3px 0.5ex 3px 0.5ex; padding: 3px 0.5ex 3px 0.5ex;
} }
#vt100 #menu .popup li.hover { #vt100 #menu .popup li.hover {
background-color: #444444; background-color: #444444;
color: white; color: white;
} }
#vt100 #menu .popup li.disabled { #vt100 #menu .popup li.disabled {
color: #AAAAAA; color: #AAAAAA;
} }
#vt100 #menu .popup hr { #vt100 #menu .popup hr {
margin: 0.5ex 0px 0.5ex 0px; margin: 0.5ex 0px 0.5ex 0px;
} }
#vt100 #menu img { #vt100 #menu img {
margin-right: 0.5ex; margin-right: 0.5ex;
width: 1ex; width: 1ex;
height: 1ex; height: 1ex;
} }
#vt100 #scrollable.inverted { color: #ffffff; #vt100 #scrollable.inverted { color: #ffffff;
background-color: #000000; } background-color: #000000; }
#vt100 #kbd_button {
float: left;
position: fixed;
z-index: 0;
visibility: hidden;
}
#vt100 #keyboard {
z-index: 3;
position: absolute;
}
#vt100 #keyboard .box {
font-family: sans-serif;
background-color: #cccccc;
padding: .8em;
float: left;
position: absolute;
border-radius: 10px;
-moz-border-radius: 10px;
box-shadow: 4px 4px 6px #222222;
-webkit-box-shadow: 4px 4px 6px #222222;
/* Don't set the -moz-box-shadow. It doesn't properly scale when CSS
* transforms are in effect. Once Firefox supports box-shadow, it should
* automatically do the right thing. Until then, leave shadows disabled
* for Firefox.
*/
opacity: 0.85;
-moz-opacity: 0.85;
filter: alpha(opacity=85);
}
#vt100 #keyboard .box * {
vertical-align: top;
display: inline-block;
}
#vt100 #keyboard b, #vt100 #keyboard i, #vt100 #keyboard s, #vt100 #keyboard u {
font-style: normal;
font-weight: bold;
border-radius: 5px;
-moz-border-radius: 5px;
background-color: #555555;
color: #eeeeee;
box-shadow: 2px 2px 3px #222222;
-webkit-box-shadow: 2px 2px 3px #222222;
padding: 4px;
margin: 2px;
height: 2ex;
display: inline-block;
text-align: center;
text-decoration: none;
}
#vt100 #keyboard b, #vt100 #keyboard s {
width: 2ex;
}
#vt100 #keyboard u, #vt100 #keyboard s {
visibility: hidden;
}
#vt100 #keyboard .shifted {
display: none;
}
#vt100 #keyboard .selected {
color: #888888;
background-color: #eeeeee;
box-shadow: 0px 0px 3px #222222;
-webkit-box-shadow: 0px 0px 3px #222222;
position: relative;
top: 1px;
left: 1px;
}
@media print { @media print {
#vt100 .scrollback { #vt100 .scrollback {
display: none; display: none;
} }
#vt100 #reconnect, #vt100 #cursor, #vt100 #menu { #vt100 #reconnect, #vt100 #cursor, #vt100 #menu, #vt100 #kbd_button, #vt100 #keyboard {
visibility: hidden; visibility: hidden;
} }
#vt100 #scrollable { #vt100 #scrollable {
overflow: hidden; overflow: hidden;
} }
#vt100 #console, #vt100 #alt_console { #vt100 #console, #vt100 #alt_console {
overflow: hidden; overflow: hidden;
width: 1000000ex; width: 1000000ex;
} }
} }

View file

@ -238,22 +238,16 @@ VT100.prototype.reset = function(clearHistory) {
this.enableAlternateScreen(false); this.enableAlternateScreen(false);
var wasCompressed = false; var wasCompressed = false;
var styles = [ 'transform', var transform = this.getTransformName();
'WebkitTransform', if (transform) {
'MozTransform', for (var i = 0; i < 2; ++i) {
'filter' ]; wasCompressed |= this.console[i].style[transform] != '';
for (var i = 0; i < styles.length; ++i) { this.console[i].style[transform] = '';
if (typeof this.console[0].style[styles[i]] != 'undefined') { }
for (var j = 0; j < 1; ++j) { this.cursor.style[transform] = '';
wasCompressed |= this.console[j].style[styles[i]] != ''; this.space.style[transform] = '';
this.console[j].style[styles[i]] = ''; if (transform == 'filter') {
} this.console[this.currentScreen].style.width = '';
this.cursor.style[styles[i]] = '';
this.space.style[styles[i]] = '';
if (styles[i] == 'filter') {
this.console[this.currentScreen].style.width = '';
}
break;
} }
} }
this.scale = 1.0; this.scale = 1.0;
@ -270,10 +264,13 @@ VT100.prototype.reset = function(clearHistory) {
}; };
VT100.prototype.addListener = function(elem, event, listener) { VT100.prototype.addListener = function(elem, event, listener) {
if (elem.addEventListener) { try {
elem.addEventListener(event, listener, false); if (elem.addEventListener) {
} else { elem.addEventListener(event, listener, false);
elem.attachEvent('on' + event, listener); } else {
elem.attachEvent('on' + event, listener);
}
} catch (e) {
} }
}; };
@ -281,11 +278,12 @@ VT100.prototype.getUserSettings = function() {
// Compute hash signature to identify the entries in the userCSS menu. // Compute hash signature to identify the entries in the userCSS menu.
// If the menu is unchanged from last time, default values can be // If the menu is unchanged from last time, default values can be
// looked up in a cookie associated with this page. // looked up in a cookie associated with this page.
this.signature = 2; this.signature = 3;
this.utfPreferred = true; this.utfPreferred = true;
this.visualBell = typeof suppressAllAudio != 'undefined' && this.visualBell = typeof suppressAllAudio != 'undefined' &&
suppressAllAudio; suppressAllAudio;
this.autoprint = true; this.autoprint = true;
this.softKeyboard = false;
this.blinkingCursor = true; this.blinkingCursor = true;
if (this.visualBell) { if (this.visualBell) {
this.signature = Math.floor(16807*this.signature + 1) % this.signature = Math.floor(16807*this.signature + 1) %
@ -311,15 +309,16 @@ VT100.prototype.getUserSettings = function() {
if (settings >= 0) { if (settings >= 0) {
settings = document.cookie.substr(settings + key.length). settings = document.cookie.substr(settings + key.length).
replace(/([0-1]*).*/, "$1"); replace(/([0-1]*).*/, "$1");
if (settings.length == 3 + (typeof userCSSList == 'undefined' ? if (settings.length == 5 + (typeof userCSSList == 'undefined' ?
0 : userCSSList.length)) { 0 : userCSSList.length)) {
this.utfPreferred = settings.charAt(0) != '0'; this.utfPreferred = settings.charAt(0) != '0';
this.visualBell = settings.charAt(1) != '0'; this.visualBell = settings.charAt(1) != '0';
this.autoprint = settings.charAt(2) != '0'; this.autoprint = settings.charAt(2) != '0';
this.blinkingCursor = settings.charAt(3) != '0'; this.softKeyboard = settings.charAt(3) != '0';
this.blinkingCursor = settings.charAt(4) != '0';
if (typeof userCSSList != 'undefined') { if (typeof userCSSList != 'undefined') {
for (var i = 0; i < userCSSList.length; ++i) { for (var i = 0; i < userCSSList.length; ++i) {
userCSSList[i][2] = settings.charAt(i + 3) != '0'; userCSSList[i][2] = settings.charAt(i + 5) != '0';
} }
} }
} }
@ -332,6 +331,7 @@ VT100.prototype.storeUserSettings = function() {
(this.utfEnabled ? '1' : '0') + (this.utfEnabled ? '1' : '0') +
(this.visualBell ? '1' : '0') + (this.visualBell ? '1' : '0') +
(this.autoprint ? '1' : '0') + (this.autoprint ? '1' : '0') +
(this.softKeyboard ? '1' : '0') +
(this.blinkingCursor ? '1' : '0'); (this.blinkingCursor ? '1' : '0');
if (typeof userCSSList != 'undefined') { if (typeof userCSSList != 'undefined') {
for (var i = 0; i < userCSSList.length; ++i) { for (var i = 0; i < userCSSList.length; ++i) {
@ -413,7 +413,7 @@ VT100.prototype.initializeUserCSSStyles = function() {
label.textContent= label.textContent; label.textContent= label.textContent;
} }
// User style sheets are number sequentially // User style sheets are numbered sequentially
var sheet = document.getElementById( var sheet = document.getElementById(
'usercss-' + i); 'usercss-' + i);
if (i == current) { if (i == current) {
@ -470,6 +470,328 @@ VT100.prototype.initializeUserCSSStyles = function() {
} }
}; };
VT100.prototype.resetLastSelectedKey = function(e) {
var key = this.lastSelectedKey;
if (!key) {
return false;
}
var position = this.mousePosition(e);
// We don't get all the necessary events to reliably reselect a key
// if we moved away from it and then back onto it. We approximate the
// behavior by remembering the key until either we release the mouse
// button (we might never get this event if the mouse has since left
// the window), or until we move away too far.
var box = this.keyboard.firstChild;
if (position[0] < box.offsetLeft + key.offsetWidth ||
position[1] < box.offsetTop + key.offsetHeight ||
position[0] >= box.offsetLeft + box.offsetWidth - key.offsetWidth ||
position[1] >= box.offsetTop + box.offsetHeight - key.offsetHeight ||
position[0] < box.offsetLeft + key.offsetLeft - key.offsetWidth ||
position[1] < box.offsetTop + key.offsetTop - key.offsetHeight ||
position[0] >= box.offsetLeft + key.offsetLeft + 2*key.offsetWidth ||
position[1] >= box.offsetTop + key.offsetTop + 2*key.offsetHeight) {
if (this.lastSelectedKey.className) log.console('reset: deselecting');
this.lastSelectedKey.className = '';
this.lastSelectedKey = undefined;
}
return false;
};
VT100.prototype.showShiftState = function(state) {
var style = document.getElementById('shift_state');
if (state) {
this.setTextContentRaw(style,
'#vt100 #keyboard .shifted {' +
'display: inline }' +
'#vt100 #keyboard .unshifted {' +
'display: none }');
} else {
this.setTextContentRaw(style, '');
}
var elems = this.keyboard.getElementsByTagName('I');
for (var i = 0; i < elems.length; ++i) {
if (elems[i].id == '16') {
elems[i].className = state ? 'selected' : '';
}
}
};
VT100.prototype.showCtrlState = function(state) {
var ctrl = this.getChildById(this.keyboard, '17' /* Ctrl */);
if (ctrl) {
ctrl.className = state ? 'selected' : '';
}
};
VT100.prototype.showAltState = function(state) {
var alt = this.getChildById(this.keyboard, '18' /* Alt */);
if (alt) {
alt.className = state ? 'selected' : '';
}
};
VT100.prototype.clickedKeyboard = function(e, elem, ch, key, shift, ctrl, alt){
var fake = [ ];
fake.charCode = ch;
fake.keyCode = key;
fake.ctrlKey = ctrl;
fake.shiftKey = shift;
fake.altKey = alt;
fake.metaKey = alt;
return this.handleKey(fake);
};
VT100.prototype.addKeyBinding = function(elem, ch, key, CH, KEY) {
if (elem == undefined) {
return;
}
if (ch == '\u00A0') {
// &nbsp; should be treated as a regular space character.
ch = ' ';
}
if (ch != undefined && CH == undefined) {
// For letter keys, we automatically compute the uppercase character code
// from the lowercase one.
CH = ch.toUpperCase();
}
if (KEY == undefined && key != undefined) {
// Most keys have identically key codes for both lowercase and uppercase
// keypresses. Normally, only function keys would have distinct key codes,
// whereas regular keys have character codes.
KEY = key;
} else if (KEY == undefined && CH != undefined) {
// For regular keys, copy the character code to the key code.
KEY = CH.charCodeAt(0);
}
if (key == undefined && ch != undefined) {
// For regular keys, copy the character code to the key code.
key = ch.charCodeAt(0);
}
// Convert characters to numeric character codes. If the character code
// is undefined (i.e. this is a function key), set it to zero.
ch = ch ? ch.charCodeAt(0) : 0;
CH = CH ? CH.charCodeAt(0) : 0;
// Mouse down events high light the key. We also set lastSelectedKey. This
// is needed to that mouseout/mouseover can keep track of the key that
// is currently being clicked.
this.addListener(elem, 'mousedown',
function(vt100, elem, key) { return function(e) {
if ((e.which || e.button) == 1) {
if (vt100.lastSelectedKey) {
vt100.lastSelectedKey.className= '';
}
// Highlight the key while the mouse button is held down.
if (key == 16 /* Shift */) {
if (!elem.className != vt100.isShift) {
vt100.showShiftState(!vt100.isShift);
}
} else if (key == 17 /* Ctrl */) {
if (!elem.className != vt100.isCtrl) {
vt100.showCtrlState(!vt100.isCtrl);
}
} else if (key == 18 /* Alt */) {
if (!elem.className != vt100.isAlt) {
vt100.showAltState(!vt100.isAlt);
}
} else {
elem.className = 'selected';
}
vt100.lastSelectedKey = elem;
}
return false; }; }(this, elem, key));
var clicked =
// Modifier keys update the state of the keyboard, but do not generate
// any key clicks that get forwarded to the application.
key >= 16 /* Shift */ && key <= 18 /* Alt */ ?
function(vt100, elem) { return function(e) {
if (elem == vt100.lastSelectedKey) {
if (key == 16 /* Shift */) {
// The user clicked the Shift key
vt100.isShift = !vt100.isShift;
vt100.showShiftState(vt100.isShift);
} else if (key == 17 /* Ctrl */) {
vt100.isCtrl = !vt100.isCtrl;
vt100.showCtrlState(vt100.isCtrl);
} else if (key == 18 /* Alt */) {
vt100.isAlt = !vt100.isAlt;
vt100.showAltState(vt100.isAlt);
}
vt100.lastSelectedKey = undefined;
}
if (vt100.lastSelectedKey) {
vt100.lastSelectedKey.className = '';
vt100.lastSelectedKey = undefined;
}
return false; }; }(this, elem) :
// Regular keys generate key clicks, when the mouse button is released or
// when a mouse click event is received.
function(vt100, elem, ch, key, CH, KEY) { return function(e) {
if (vt100.lastSelectedKey) {
if (elem == vt100.lastSelectedKey) {
// The user clicked a key.
if (vt100.isShift) {
vt100.clickedKeyboard(e, elem, CH, KEY,
true, vt100.isCtrl, vt100.isAlt);
} else {
vt100.clickedKeyboard(e, elem, ch, key,
false, vt100.isCtrl, vt100.isAlt);
}
vt100.isShift = false;
vt100.showShiftState(false);
vt100.isCtrl = false;
vt100.showCtrlState(false);
vt100.isAlt = false;
vt100.showAltState(false);
}
vt100.lastSelectedKey.className = '';
vt100.lastSelectedKey = undefined;
}
elem.className = '';
return false; }; }(this, elem, ch, key, CH, KEY);
this.addListener(elem, 'mouseup', clicked);
this.addListener(elem, 'click', clicked);
// When moving the mouse away from a key, check if any keys need to be
// deselected.
this.addListener(elem, 'mouseout',
function(vt100, elem, key) { return function(e) {
if (key == 16 /* Shift */) {
if (!elem.className == vt100.isShift) {
vt100.showShiftState(vt100.isShift);
}
} else if (key == 17 /* Ctrl */) {
if (!elem.className == vt100.isCtrl) {
vt100.showCtrlState(vt100.isCtrl);
}
} else if (key == 18 /* Alt */) {
if (!elem.className == vt100.isAlt) {
vt100.showAltState(vt100.isAlt);
}
} else if (elem.className) {
elem.className = '';
vt100.lastSelectedKey = elem;
} else if (vt100.lastSelectedKey) {
vt100.resetLastSelectedKey(e);
}
return false; }; }(this, elem, key));
// When moving the mouse over a key, select it if the user is still holding
// the mouse button down (i.e. elem == lastSelectedKey)
this.addListener(elem, 'mouseover',
function(vt100, elem, key) { return function(e) {
if (elem == vt100.lastSelectedKey) {
if (key == 16 /* Shift */) {
if (!elem.className != vt100.isShift) {
vt100.showShiftState(!vt100.isShift);
}
} else if (key == 17 /* Ctrl */) {
if (!elem.className != vt100.isCtrl) {
vt100.showCtrlState(!vt100.isCtrl);
}
} else if (key == 18 /* Alt */) {
if (!elem.className != vt100.isAlt) {
vt100.showAltState(!vt100.isAlt);
}
} else if (!elem.className) {
elem.className = 'selected';
}
} else {
vt100.resetLastSelectedKey(e);
}
return false; }; }(this, elem, key));
};
VT100.prototype.initializeKeyBindings = function(elem) {
if (elem) {
if (elem.nodeName == "I" || elem.nodeName == "B") {
if (elem.id) {
// Function keys. The Javascript keycode is part of the "id"
var i = parseInt(elem.id);
if (i) {
// If the id does not parse as a number, it is not a keycode.
this.addKeyBinding(elem, undefined, i);
}
} else {
var child = elem.firstChild;
if (child.nodeName == "#text") {
// If the key only has a text node as a child, then it is a letter.
// Automatically compute the lower and upper case version of the key.
this.addKeyBinding(elem, this.getTextContent(child).toLowerCase());
} else {
// If the key has two children, they are the lower and upper case
// character code, respectively.
this.addKeyBinding(elem, this.getTextContent(child), undefined,
this.getTextContent(child.nextSibling));
}
}
}
}
// Recursively parse all other child nodes.
for (elem = elem.firstChild; elem; elem = elem.nextSibling) {
this.initializeKeyBindings(elem);
}
};
VT100.prototype.initializeKeyboard = function() {
// Configure mouse event handlers for button that displays/hides keyboard
var box = this.keyboard.firstChild;
this.hideSoftKeyboard();
this.addListener(this.keyboardImage, 'click',
function(vt100) { return function(e) {
if (vt100.keyboard.style.display != '') {
if (vt100.reconnectBtn.style.visibility != '') {
vt100.showSoftKeyboard();
}
} else {
vt100.hideSoftKeyboard();
vt100.input.focus();
}
return false; }; }(this));
// Enable button that displays keyboard
if (this.softKeyboard) {
this.keyboardImage.style.visibility = 'visible';
}
// Configure mouse event handlers for on-screen keyboard
this.addListener(this.keyboard, 'click',
function(vt100) { return function(e) {
vt100.hideSoftKeyboard();
vt100.input.focus();
return false; }; }(this));
this.addListener(this.keyboard, 'selectstart', this.cancelEvent);
this.addListener(box, 'click', this.cancelEvent);
this.addListener(box, 'mouseup',
function(vt100) { return function(e) {
if (vt100.lastSelectedKey) {
vt100.lastSelectedKey.className = '';
vt100.lastSelectedKey = undefined;
}
return false; }; }(this));
this.addListener(box, 'mouseout',
function(vt100) { return function(e) {
return vt100.resetLastSelectedKey(e); }; }(this));
this.addListener(box, 'mouseover',
function(vt100) { return function(e) {
return vt100.resetLastSelectedKey(e); }; }(this));
// Configure SHIFT key behavior
var style = document.createElement('style');
var id = document.createAttribute('id');
id.nodeValue = 'shift_state';
style.setAttributeNode(id);
var type = document.createAttribute('type');
type.nodeValue = 'text/css';
style.setAttributeNode(type);
document.getElementsByTagName('head')[0].appendChild(style);
// Set up key bindings
this.initializeKeyBindings(box);
};
VT100.prototype.initializeElements = function(container) { VT100.prototype.initializeElements = function(container) {
// If the necessary objects have not already been defined in the HTML // If the necessary objects have not already been defined in the HTML
// page, create them now. // page, create them now.
@ -483,6 +805,9 @@ VT100.prototype.initializeElements = function(container) {
if (!this.getChildById(this.container, 'reconnect') || if (!this.getChildById(this.container, 'reconnect') ||
!this.getChildById(this.container, 'menu') || !this.getChildById(this.container, 'menu') ||
!this.getChildById(this.container, 'keyboard') ||
!this.getChildById(this.container, 'kbd_button') ||
!this.getChildById(this.container, 'kbd_img') ||
!this.getChildById(this.container, 'scrollable') || !this.getChildById(this.container, 'scrollable') ||
!this.getChildById(this.container, 'console') || !this.getChildById(this.container, 'console') ||
!this.getChildById(this.container, 'alt_console') || !this.getChildById(this.container, 'alt_console') ||
@ -525,7 +850,15 @@ VT100.prototype.initializeElements = function(container) {
'<div id="cursize" style="visibility: hidden">' + '<div id="cursize" style="visibility: hidden">' +
'</div>' + '</div>' +
'<div id="menu"></div>' + '<div id="menu"></div>' +
'<div id="keyboard" unselectable="on">' +
'<pre class="box"><div><i id="27">Esc</i><i id="112">F1</i><i id="113">F2</i><i id="114">F3</i><i id="115">F4</i><i id="116">F5</i><i id="117">F6</i><i id="118">F7</i><i id="119">F8</i><i id="120">F9</i><i id="121">F10</i><i id="122">F11</i><i id="123">F12</i><br /><b><span class="unshifted">`</span><span class="shifted">~</span></b><b><span class="unshifted">1</span><span class="shifted">!</span></b><b><span class="unshifted">2</span><span class="shifted">@</span></b><b><span class="unshifted">3</span><span class="shifted">#</span></b><b><span class="unshifted">4</span><span class="shifted">&#36;</span></b><b><span class="unshifted">5</span><span class="shifted">&#37;</span></b><b><span class="unshifted">6</span><span class="shifted">^</span></b><b><span class="unshifted">7</span><span class="shifted">&amp;</span></b><b><span class="unshifted">8</span><span class="shifted">*</span></b><b><span class="unshifted">9</span><span class="shifted">(</span></b><b><span class="unshifted">0</span><span class="shifted">)</span></b><b><span class="unshifted">-</span><span class="shifted">_</span></b><b><span class="unshifted">=</span><span class="shifted">+</span></b><i id="8">&nbsp;&larr;&nbsp;</i><br /><i id="9">Tab</i><b>Q</b><b>W</b><b>E</b><b>R</b><b>T</b><b>Y</b><b>U</b><b>I</b><b>O</b><b>P</b><b><span class="unshifted">[</span><span class="shifted">{</span></b><b><span class="unshifted">]</span><span class="shifted">}</span></b><b><span class="unshifted">&#92;</span><span class="shifted">|</span></b><br /><u>Tab&nbsp;&nbsp;</u><b>A</b><b>S</b><b>D</b><b>F</b><b>G</b><b>H</b><b>J</b><b>K</b><b>L</b><b><span class="unshifted">;</span><span class="shifted">:</span></b><b><span class="unshifted">&#39;</span><span class="shifted">"</span></b><i id="13">Enter</i><br /><u>&nbsp;&nbsp;</u><i id="16">Shift</i><b>Z</b><b>X</b><b>C</b><b>V</b><b>B</b><b>N</b><b>M</b><b><span class="unshifted">,</span><span class="shifted">&lt;</span></b><b><span class="unshifted">.</span><span class="shifted">&gt;</span></b><b><span class="unshifted">/</span><span class="shifted">?</span></b><i id="16">Shift</i><br /><u>XXX</u><i id="17">Ctrl</i><i id="18">Alt</i><i style="width: 25ex">&nbsp</i></div>&nbsp;&nbsp;&nbsp;<div><i id="45">Ins</i><i id="46">Del</i><i id="36">Home</i><i id="35">End</i><br /><u>&nbsp;</u><br /><u>&nbsp;</u><br /><u>Ins</u><s>&nbsp;</s><b id="38">&uarr;</b><s>&nbsp;</s><u>&nbsp;</u><b id="33">&uArr;</b><br /><u>Ins</u><b id="37">&larr;</b><b id="40">&darr;</b><b id="39">&rarr;</b><u>&nbsp;</u><b id="34">&dArr;</b></div></pre>' +
'</div>' +
'<div id="scrollable">' + '<div id="scrollable">' +
'<table id="kbd_button">' +
'<tr><td width="100%">&nbsp;</td>' +
'<td><img id="kbd_img" src="keyboard.png" /></td>' +
'<td>&nbsp;&nbsp;&nbsp;&nbsp;</td></tr>' +
'</table>' +
'<pre id="lineheight">&nbsp;</pre>' + '<pre id="lineheight">&nbsp;</pre>' +
'<pre id="console">' + '<pre id="console">' +
'<pre></pre>' + '<pre></pre>' +
@ -566,6 +899,8 @@ VT100.prototype.initializeElements = function(container) {
this.reconnectBtn = this.getChildById(this.container,'reconnect'); this.reconnectBtn = this.getChildById(this.container,'reconnect');
this.curSizeBox = this.getChildById(this.container, 'cursize'); this.curSizeBox = this.getChildById(this.container, 'cursize');
this.menu = this.getChildById(this.container, 'menu'); this.menu = this.getChildById(this.container, 'menu');
this.keyboard = this.getChildById(this.container, 'keyboard');
this.keyboardImage = this.getChildById(this.container, 'kbd_img');
this.scrollable = this.getChildById(this.container, this.scrollable = this.getChildById(this.container,
'scrollable'); 'scrollable');
this.lineheight = this.getChildById(this.container, this.lineheight = this.getChildById(this.container,
@ -646,6 +981,9 @@ VT100.prototype.initializeElements = function(container) {
// Hide context menu // Hide context menu
this.hideContextMenu(); this.hideContextMenu();
// Set up onscreen soft keyboard
this.initializeKeyboard();
// Add listener to reconnect button // Add listener to reconnect button
this.addListener(this.reconnectBtn.firstChild, 'click', this.addListener(this.reconnectBtn.firstChild, 'click',
function(vt100) { function(vt100) {
@ -733,6 +1071,7 @@ VT100.prototype.reconnect = function() {
VT100.prototype.showReconnect = function(state) { VT100.prototype.showReconnect = function(state) {
if (state) { if (state) {
this.hideSoftKeyboard();
this.reconnectBtn.style.visibility = ''; this.reconnectBtn.style.visibility = '';
} else { } else {
this.reconnectBtn.style.visibility = 'hidden'; this.reconnectBtn.style.visibility = 'hidden';
@ -766,6 +1105,9 @@ VT100.prototype.resized = function(w, h) {
}; };
VT100.prototype.resizer = function() { VT100.prototype.resizer = function() {
// Hide onscreen soft keyboard
this.hideSoftKeyboard();
// The cursor can get corrupted if the print-preview is displayed in Firefox. // The cursor can get corrupted if the print-preview is displayed in Firefox.
// Recreating it, will repair it. // Recreating it, will repair it.
var newCursor = document.createElement('pre'); var newCursor = document.createElement('pre');
@ -945,6 +1287,17 @@ VT100.prototype.cancelEvent = function(event) {
return false; return false;
}; };
VT100.prototype.mousePosition = function(event) {
var offsetX = this.container.offsetLeft;
var offsetY = this.container.offsetTop;
for (var e = this.container; e = e.offsetParent; ) {
offsetX += e.offsetLeft;
offsetY += e.offsetTop;
}
return [ event.clientX - offsetX,
event.clientY - offsetY ];
};
VT100.prototype.mouseEvent = function(event, type) { VT100.prototype.mouseEvent = function(event, type) {
// If any text is currently selected, do not move the focus as that would // If any text is currently selected, do not move the focus as that would
// invalidate the selection. // invalidate the selection.
@ -954,15 +1307,10 @@ VT100.prototype.mouseEvent = function(event, type) {
} }
// Compute mouse position in characters. // Compute mouse position in characters.
var offsetX = this.container.offsetLeft; var position = this.mousePosition(event);
var offsetY = this.container.offsetTop; var x = Math.floor(position[0] / this.cursorWidth);
for (var e = this.container; e = e.offsetParent; ) { var y = Math.floor((position[1] + this.scrollable.scrollTop) /
offsetX += e.offsetLeft; this.cursorHeight) - this.numScrollbackLines;
offsetY += e.offsetTop;
}
var x = (event.clientX - offsetX) / this.cursorWidth;
var y = ((event.clientY - offsetY) + this.scrollable.offsetTop) /
this.cursorHeight - this.numScrollbackLines;
var inside = true; var inside = true;
if (x >= this.terminalWidth) { if (x >= this.terminalWidth) {
x = this.terminalWidth - 1; x = this.terminalWidth - 1;
@ -1022,7 +1370,7 @@ VT100.prototype.mouseEvent = function(event, type) {
// Bring up context menu. // Bring up context menu.
if (button == 2 && !event.shiftKey) { if (button == 2 && !event.shiftKey) {
if (type == 0 /* MOUSE_DOWN */) { if (type == 0 /* MOUSE_DOWN */) {
this.showContextMenu(event.clientX - offsetX, event.clientY - offsetY); this.showContextMenu(position[0], position[1]);
} }
return this.cancelEvent(event); return this.cancelEvent(event);
} }
@ -1058,6 +1406,29 @@ VT100.prototype.getTextContent = function(elem) {
(typeof elem.textContent == 'undefined' ? elem.innerText : ''); (typeof elem.textContent == 'undefined' ? elem.innerText : '');
}; };
VT100.prototype.setTextContentRaw = function(elem, s) {
// Updating the content of an element is an expensive operation. It actually
// pays off to first check whether the element is still unchanged.
if (typeof elem.textContent == 'undefined') {
if (elem.innerText != s) {
try {
elem.innerText = s;
} catch (e) {
// Very old versions of IE do not allow setting innerText. Instead,
// remove all children, by setting innerHTML and then set the text
// using DOM methods.
elem.innerHTML = '';
elem.appendChild(document.createTextNode(
this.replaceChar(s, ' ', '\u00A0')));
}
}
} else {
if (elem.textContent != s) {
elem.textContent = s;
}
}
};
VT100.prototype.setTextContent = function(elem, s) { VT100.prototype.setTextContent = function(elem, s) {
// Check if we find any URLs in the text. If so, automatically convert them // Check if we find any URLs in the text. If so, automatically convert them
// to links. // to links.
@ -1103,26 +1474,7 @@ VT100.prototype.setTextContent = function(elem, s) {
return; return;
} }
// Updating the content of an element is an expensive operation. It actually this.setTextContentRaw(elem, s);
// pays off to first check whether the element is still unchanged.
if (typeof elem.textContent == 'undefined') {
if (elem.innerText != s) {
try {
elem.innerText = s;
} catch (e) {
// Very old versions of IE do not allow setting innerText. Instead,
// remove all children, by setting innerHTML and then set the text
// using DOM methods.
elem.innerHTML = '';
elem.appendChild(document.createTextNode(
this.replaceChar(s, ' ', '\u00A0')));
}
}
} else {
if (elem.textContent != s) {
elem.textContent = s;
}
}
}; };
VT100.prototype.insertBlankLine = function(y, color, style) { VT100.prototype.insertBlankLine = function(y, color, style) {
@ -1578,27 +1930,21 @@ VT100.prototype.enableAlternateScreen = function(state) {
this.console[this.currentScreen].style.display = ''; this.console[this.currentScreen].style.display = '';
// Select appropriate character pitch. // Select appropriate character pitch.
var styles = [ 'transform', var transform = this.getTransformName();
'WebkitTransform', if (transform) {
'MozTransform', if (state) {
'filter' ]; // Upon enabling the alternate screen, we switch to 80 column mode. But
for (var i = 0; i < styles.length; ++i) { // upon returning to the regular screen, we restore the mode that was
if (typeof this.console[0].style[styles[i]] != 'undefined') { // in effect previously.
if (state) { this.console[1].style[transform] = '';
// Upon enabling the alternate screen, we switch to 80 column mode. But }
// upon returning to the regular screen, we restore the mode that was var style =
// in effect previously. this.console[this.currentScreen].style[transform];
this.console[1].style[styles[i]] = ''; this.cursor.style[transform] = style;
} this.space.style[transform] = style;
var style = this.scale = style == '' ? 1.0:1.65;
this.console[this.currentScreen].style[styles[i]]; if (transform == 'filter') {
this.cursor.style[styles[i]] = style; this.console[this.currentScreen].style.width = style == '' ? '165%':'';
this.space.style[styles[i]] = style;
this.scale = style == '' ? 1.0:1.65;
if (styles[i] == 'filter') {
this.console[this.currentScreen].style.width = style == '' ? '165%':'';
}
break;
} }
} }
this.resizer(); this.resizer();
@ -1969,12 +2315,76 @@ VT100.prototype.toggleBell = function() {
this.visualBell = !this.visualBell; this.visualBell = !this.visualBell;
}; };
VT100.prototype.toggleSoftKeyboard = function() {
this.softKeyboard = !this.softKeyboard;
this.keyboardImage.style.visibility = this.softKeyboard ? 'visible' : '';
};
VT100.prototype.deselectKeys = function(elem) {
if (elem && elem.className == 'selected') {
elem.className = '';
}
for (elem = elem.firstChild; elem; elem = elem.nextSibling) {
this.deselectKeys(elem);
}
};
VT100.prototype.showSoftKeyboard = function() {
// Make sure no key is currently selected
this.lastSelectedKey = undefined;
this.deselectKeys(this.keyboard);
this.isShift = false;
this.showShiftState(false);
this.isCtrl = false;
this.showCtrlState(false);
this.isAlt = false;
this.showAltState(false);
this.keyboard.style.left = '0px';
this.keyboard.style.top = '0px';
this.keyboard.style.width = this.container.offsetWidth + 'px';
this.keyboard.style.height = this.container.offsetHeight + 'px';
this.keyboard.style.visibility = 'hidden';
this.keyboard.style.display = '';
var kbd = this.keyboard.firstChild;
var scale = 1.0;
var transform = this.getTransformName();
if (transform) {
kbd.style[transform] = '';
if (kbd.offsetWidth > 0.9 * this.container.offsetWidth) {
scale = (kbd.offsetWidth/
this.container.offsetWidth)/0.9;
}
if (kbd.offsetHeight > 0.9 * this.container.offsetHeight) {
scale = Math.max((kbd.offsetHeight/
this.container.offsetHeight)/0.9);
}
var style = this.getTransformStyle(transform,
scale > 1.0 ? scale : undefined);
kbd.style[transform] = style;
}
if (transform == 'filter') {
scale = 1.0;
}
kbd.style.left = ((this.container.offsetWidth -
kbd.offsetWidth/scale)/2) + 'px';
kbd.style.top = ((this.container.offsetHeight -
kbd.offsetHeight/scale)/2) + 'px';
this.keyboard.style.visibility = 'visible';
};
VT100.prototype.hideSoftKeyboard = function() {
this.keyboard.style.display = 'none';
};
VT100.prototype.toggleCursorBlinking = function() { VT100.prototype.toggleCursorBlinking = function() {
this.blinkingCursor = !this.blinkingCursor; this.blinkingCursor = !this.blinkingCursor;
}; };
VT100.prototype.about = function() { VT100.prototype.about = function() {
alert("VT100 Terminal Emulator " + "2.10 (revision 220)" + alert("VT100 Terminal Emulator " + "2.10 (revision 221)" +
"\nCopyright 2008-2010 by Markus Gutschke\n" + "\nCopyright 2008-2010 by Markus Gutschke\n" +
"For more information check http://shellinabox.com"); "For more information check http://shellinabox.com");
}; };
@ -2007,6 +2417,9 @@ VT100.prototype.showContextMenu = function(x, y) {
'<li>' + '<li>' +
(this.visualBell ? '<img src="enabled.gif" />' : '') + (this.visualBell ? '<img src="enabled.gif" />' : '') +
'Visual Bell</li>'+ 'Visual Bell</li>'+
'<li>' +
(this.softKeyboard ? '<img src="enabled.gif" />' : '') +
'Onscreen Keyboard</li>' +
'<li id="endconfig">' + '<li id="endconfig">' +
(this.blinkingCursor ? '<img src="enabled.gif" />' : '') + (this.blinkingCursor ? '<img src="enabled.gif" />' : '') +
'Blinking Cursor</li>'+ 'Blinking Cursor</li>'+
@ -2038,6 +2451,7 @@ VT100.prototype.showContextMenu = function(x, y) {
// Actions for default items // Actions for default items
var actions = [ this.copyLast, p, this.reset, var actions = [ this.copyLast, p, this.reset,
this.toggleUTF, this.toggleBell, this.toggleUTF, this.toggleBell,
this.toggleSoftKeyboard,
this.toggleCursorBlinking ]; this.toggleCursorBlinking ];
// Actions for user CSS styles (if any) // Actions for user CSS styles (if any)
@ -2093,26 +2507,30 @@ VT100.prototype.showContextMenu = function(x, y) {
} }
// Position menu next to the mouse pointer // Position menu next to the mouse pointer
if (x + popup.clientWidth > this.container.offsetWidth) { this.menu.style.left = '0px';
x = this.container.offsetWidth - popup.clientWidth; this.menu.style.top = '0px';
this.menu.style.width = this.container.offsetWidth + 'px';
this.menu.style.height = this.container.offsetHeight + 'px';
popup.style.left = '0px';
popup.style.top = '0px';
var margin = 2;
if (x + popup.clientWidth >= this.container.offsetWidth - margin) {
x = this.container.offsetWidth-popup.clientWidth - margin - 1;
} }
if (x < 0) { if (x < margin) {
x = 0; x = margin;
} }
if (y + popup.clientHeight > this.container.offsetHeight) { if (y + popup.clientHeight >= this.container.offsetHeight - margin) {
y = this.container.offsetHeight-popup.clientHeight; y = this.container.offsetHeight-popup.clientHeight - margin - 1;
} }
if (y < 0) { if (y < margin) {
y = 0; y = margin;
} }
popup.style.left = x + 'px'; popup.style.left = x + 'px';
popup.style.top = y + 'px'; popup.style.top = y + 'px';
// Block all other interactions with the terminal emulator // Block all other interactions with the terminal emulator
this.menu.style.left = '0px';
this.menu.style.top = '0px';
this.menu.style.width = this.container.offsetWidth + 'px';
this.menu.style.height = this.container.offsetHeight + 'px';
this.addListener(this.menu, 'click', function(vt100) { this.addListener(this.menu, 'click', function(vt100) {
return function() { return function() {
vt100.hideContextMenu(); vt100.hideContextMenu();
@ -2895,39 +3313,42 @@ VT100.prototype.restoreCursor = function() {
this.savedY[this.currentScreen]); this.savedY[this.currentScreen]);
}; };
VT100.prototype.set80_132Mode = function(state) { VT100.prototype.getTransformName = function() {
var transform = undefined; var styles = [ 'transform', 'WebkitTransform', 'MozTransform', 'filter' ];
var styles = [ 'transform',
'WebkitTransform',
'MozTransform',
'filter'
];
for (var i = 0; i < styles.length; ++i) { for (var i = 0; i < styles.length; ++i) {
if (typeof this.console[0].style[styles[i]] != 'undefined') { if (typeof this.console[0].style[styles[i]] != 'undefined') {
transform = styles[i]; return styles[i];
break;
} }
} }
return undefined;
};
VT100.prototype.getTransformStyle = function(transform, scale) {
return scale && scale != 1.0
? transform == 'filter'
? 'progid:DXImageTransform.Microsoft.Matrix(' +
'M11=' + (1.0/scale) + ',M12=0,M21=0,M22=1,' +
"sizingMethod='auto expand')"
: 'translateX(-50%) ' +
'scaleX(' + (1.0/scale) + ') ' +
'translateX(50%)'
: '';
};
VT100.prototype.set80_132Mode = function(state) {
var transform = this.getTransformName();
if (transform) { if (transform) {
if ((this.console[this.currentScreen].style[transform] != '') == state) { if ((this.console[this.currentScreen].style[transform] != '') == state) {
return; return;
} }
var style = var style = state ?
state ? transform == 'filter' this.getTransformStyle(transform, 1.65):'';
? 'progid:DXImageTransform.Microsoft.Matrix(' +
'M11=0.606060606060606060606,M12=0,M21=0,M22=1,' +
"sizingMethod='auto expand')"
: 'translateX(-50%) ' +
'scaleX(0.606060606060606060606) ' +
'translateX(50%)'
: '';
this.console[this.currentScreen].style[transform] = style; this.console[this.currentScreen].style[transform] = style;
this.cursor.style[transform] = style; this.cursor.style[transform] = style;
this.space.style[transform] = style; this.space.style[transform] = style;
this.scale = state ? 1.65 : 1.0; this.scale = state ? 1.65 : 1.0;
if (transform == 'filter') { if (transform == 'filter') {
this.console[this.currentScreen].style.width = state ? '165%' : ''; this.console[this.currentScreen].style.width = state ? '165%' : '';
} }
this.resizer(); this.resizer();
} }

View file

@ -0,0 +1,59 @@
<pre class="box">
<div>
<i id="27">Esc</i><i id="112">F1</i><i id="113">F2</i><i id="114">F3</i>
<i id="115">F4</i><i id="116">F5</i><i id="117">F6</i><i id="118">F7</i>
<i id="119">F8</i><i id="120">F9</i><i id="121">F10</i><i id="122">F11</i>
<i id="123">F12</i><br />
<b><span class="unshifted">`</span><span class="shifted">~</span></b>
<b><span class="unshifted">1</span><span class="shifted">!</span></b>
<b><span class="unshifted">2</span><span class="shifted">@</span></b>
<b><span class="unshifted">3</span><span class="shifted">#</span></b>
<b><span class="unshifted">4</span><span class="shifted">&#36;</span></b>
<b><span class="unshifted">5</span><span class="shifted">&#37;</span></b>
<b><span class="unshifted">6</span><span class="shifted">^</span></b>
<b><span class="unshifted">7</span><span class="shifted">&amp;</span></b>
<b><span class="unshifted">8</span><span class="shifted">*</span></b>
<b><span class="unshifted">9</span><span class="shifted">(</span></b>
<b><span class="unshifted">0</span><span class="shifted">)</span></b>
<b><span class="unshifted">-</span><span class="shifted">_</span></b>
<b><span class="unshifted">=</span><span class="shifted">+</span></b>
<i id="8">&nbsp;&larr;&nbsp;</i>
<br />
<i id="9">Tab</i>
<b>Q</b><b>W</b><b>E</b><b>R</b><b>T</b><b>Y</b><b>U</b><b>I</b><b>O</b>
<b>P</b>
<b><span class="unshifted">[</span><span class="shifted">{</span></b>
<b><span class="unshifted">]</span><span class="shifted">}</span></b>
<b><span class="unshifted">&#92;</span><span class="shifted">|</span></b>
<br />
<u>Tab&nbsp;&nbsp;</u>
<b>A</b><b>S</b><b>D</b><b>F</b><b>G</b><b>H</b><b>J</b><b>K</b><b>L</b>
<b><span class="unshifted">;</span><span class="shifted">:</span></b>
<b><span class="unshifted">&#39;</span><span class="shifted">"</span></b>
<i id="13">Enter</i>
<br />
<u>&nbsp;&nbsp;</u>
<i id="16">Shift</i>
<b>Z</b><b>X</b><b>C</b><b>V</b><b>B</b><b>N</b><b>M</b>
<b><span class="unshifted">,</span><span class="shifted">&lt;</span></b>
<b><span class="unshifted">.</span><span class="shifted">&gt;</span></b>
<b><span class="unshifted">/</span><span class="shifted">?</span></b>
<i id="16">Shift</i>
<br />
<u>XXX</u>
<i id="17">Ctrl</i>
<i id="18">Alt</i>
<i style="width: 25ex">&nbsp</i>
</div>
&nbsp;&nbsp;&nbsp;
<div>
<i id="45">Ins</i><i id="46">Del</i><i id="36">Home</i><i id="35">End</i>
<br />
<u>&nbsp;</u><br />
<u>&nbsp;</u><br />
<u>Ins</u><s>&nbsp;</s><b id="38">&uarr;</b><s>&nbsp;</s><u>&nbsp;</u>
<b id="33">&uArr;</b><br />
<u>Ins</u><b id="37">&larr;</b><b id="40">&darr;</b>
<b id="39">&rarr;</b><u>&nbsp;</u><b id="34">&dArr;</b>
</div>
</pre>

BIN
shellinabox/keyboard.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 808 B

View file

@ -358,7 +358,7 @@ ShellInABox.prototype.extendContextMenu = function(entries, actions) {
}; };
ShellInABox.prototype.about = function() { ShellInABox.prototype.about = function() {
alert("Shell In A Box version " + "2.10 (revision 220)" + alert("Shell In A Box version " + "2.10 (revision 221)" +
"\nCopyright 2008-2010 by Markus Gutschke\n" + "\nCopyright 2008-2010 by Markus Gutschke\n" +
"For more information check http://shellinabox.com" + "For more information check http://shellinabox.com" +
(typeof serverSupportsSSL != 'undefined' && serverSupportsSSL ? (typeof serverSupportsSSL != 'undefined' && serverSupportsSSL ?

View file

@ -641,6 +641,11 @@ static int shellInABoxHttpHandler(HttpConnection *http, void *arg,
extern char faviconStart[]; extern char faviconStart[];
extern char faviconEnd[]; extern char faviconEnd[];
serveStaticFile(http, "image/x-icon", faviconStart, faviconEnd); serveStaticFile(http, "image/x-icon", faviconStart, faviconEnd);
} else if (pathInfoLength == 12 && !memcmp(pathInfo, "keyboard.png", 11)) {
// Serve the keyboard icon
extern char keyboardStart[];
extern char keyboardEnd[];
serveStaticFile(http, "image/png", keyboardStart, keyboardEnd);
} else if (pathInfoLength == 14 && !memcmp(pathInfo, "ShellInABox.js", 14)) { } else if (pathInfoLength == 14 && !memcmp(pathInfo, "ShellInABox.js", 14)) {
// Serve both vt100.js and shell_in_a_box.js in the same transaction. // Serve both vt100.js and shell_in_a_box.js in the same transaction.
// Also, indicate to the client whether the server is SSL enabled. // Also, indicate to the client whether the server is SSL enabled.

View file

@ -1,140 +1,217 @@
#vt100 a { #vt100 a {
text-decoration: none; text-decoration: none;
color: inherit; color: inherit;
} }
#vt100 a:hover { #vt100 a:hover {
text-decoration: underline; text-decoration: underline;
} }
#vt100 #reconnect { #vt100 #reconnect {
position: absolute; position: absolute;
z-index: 2; z-index: 2;
} }
#vt100 #reconnect input { #vt100 #reconnect input {
padding: 1ex; padding: 1ex;
font-weight: bold; font-weight: bold;
font-size: x-large; font-size: x-large;
} }
#vt100 #cursize { #vt100 #cursize {
background: #EEEEEE; background: #EEEEEE;
border: 1px solid black; border: 1px solid black;
font-family: sans-serif; font-family: sans-serif;
font-size: large; font-size: large;
font-weight: bold; font-weight: bold;
padding: 1ex; padding: 1ex;
position: absolute; position: absolute;
z-index: 2; z-index: 2;
} }
#vt100 pre { #vt100 pre {
margin: 0px; margin: 0px;
} }
#vt100 pre pre { #vt100 pre pre {
overflow: hidden; overflow: hidden;
} }
#vt100 #scrollable { #vt100 #scrollable {
overflow-x: hidden; overflow-x: hidden;
overflow-y: scroll; overflow-y: scroll;
position: relative; position: relative;
padding: 1px; padding: 1px;
} }
#vt100 #console, #vt100 #alt_console, #vt100 #cursor, #vt100 #lineheight, #vt100 .hidden pre { #vt100 #console, #vt100 #alt_console, #vt100 #cursor, #vt100 #lineheight, #vt100 .hidden pre {
font-family: "DejaVu Sans Mono", "Everson Mono", FreeMono, "Andale Mono", "Lucida Console", monospace; font-family: "DejaVu Sans Mono", "Everson Mono", FreeMono, "Andale Mono", "Lucida Console", monospace;
} }
#vt100 #lineheight { #vt100 #lineheight {
position: absolute; position: absolute;
visibility: hidden; visibility: hidden;
} }
#vt100 #cursor { #vt100 #cursor {
position: absolute; position: absolute;
left: 0px; left: 0px;
top: 0px; top: 0px;
overflow: hidden; overflow: hidden;
z-index: 1; z-index: 1;
} }
#vt100 #cursor.bright { #vt100 #cursor.bright {
background-color: #e60000; background-color: #e60000;
color: white; color: white;
} }
#vt100 #cursor.dim { #vt100 #cursor.dim {
visibility: hidden; visibility: hidden;
} }
#vt100 #cursor.inactive { #vt100 #cursor.inactive {
border: 1px solid #e60000; border: 1px solid #e60000;
margin: -1px; margin: -1px;
} }
#vt100 #padding { #vt100 #padding {
visibility: hidden; visibility: hidden;
width: 1px; width: 1px;
height: 0px; height: 0px;
overflow: hidden; overflow: hidden;
} }
#vt100 .hidden { #vt100 .hidden {
position: absolute; position: absolute;
top: -10000px; top: -10000px;
left: -10000px; left: -10000px;
width: 0px; width: 0px;
height: 0px; height: 0px;
} }
#vt100 #menu { #vt100 #menu {
overflow: visible; overflow: visible;
position: absolute; position: absolute;
z-index: 3; z-index: 3;
} }
#vt100 #menu .popup { #vt100 #menu .popup {
background-color: #EEEEEE; background-color: #EEEEEE;
border: 1px solid black; border: 1px solid black;
font-family: sans-serif; font-family: sans-serif;
position: absolute; position: absolute;
} }
#vt100 #menu .popup ul { #vt100 #menu .popup ul {
list-style-type: none; list-style-type: none;
padding: 0px; padding: 0px;
margin: 0px; margin: 0px;
min-width: 10em; min-width: 10em;
} }
#vt100 #menu .popup li { #vt100 #menu .popup li {
padding: 3px 0.5ex 3px 0.5ex; padding: 3px 0.5ex 3px 0.5ex;
} }
#vt100 #menu .popup li.hover { #vt100 #menu .popup li.hover {
background-color: #444444; background-color: #444444;
color: white; color: white;
} }
#vt100 #menu .popup li.disabled { #vt100 #menu .popup li.disabled {
color: #AAAAAA; color: #AAAAAA;
} }
#vt100 #menu .popup hr { #vt100 #menu .popup hr {
margin: 0.5ex 0px 0.5ex 0px; margin: 0.5ex 0px 0.5ex 0px;
} }
#vt100 #menu img { #vt100 #menu img {
margin-right: 0.5ex; margin-right: 0.5ex;
width: 1ex; width: 1ex;
height: 1ex; height: 1ex;
} }
#vt100 #scrollable.inverted { color: #ffffff; #vt100 #scrollable.inverted { color: #ffffff;
background-color: #000000; } background-color: #000000; }
#vt100 #kbd_button {
float: left;
position: fixed;
z-index: 0;
visibility: hidden;
}
#vt100 #keyboard {
z-index: 3;
position: absolute;
}
#vt100 #keyboard .box {
font-family: sans-serif;
background-color: #cccccc;
padding: .8em;
float: left;
position: absolute;
border-radius: 10px;
-moz-border-radius: 10px;
box-shadow: 4px 4px 6px #222222;
-webkit-box-shadow: 4px 4px 6px #222222;
/* Don't set the -moz-box-shadow. It doesn't properly scale when CSS
* transforms are in effect. Once Firefox supports box-shadow, it should
* automatically do the right thing. Until then, leave shadows disabled
* for Firefox.
*/
opacity: 0.85;
-moz-opacity: 0.85;
filter: alpha(opacity=85);
}
#vt100 #keyboard .box * {
vertical-align: top;
display: inline-block;
}
#vt100 #keyboard b, #vt100 #keyboard i, #vt100 #keyboard s, #vt100 #keyboard u {
font-style: normal;
font-weight: bold;
border-radius: 5px;
-moz-border-radius: 5px;
background-color: #555555;
color: #eeeeee;
box-shadow: 2px 2px 3px #222222;
-webkit-box-shadow: 2px 2px 3px #222222;
padding: 4px;
margin: 2px;
height: 2ex;
display: inline-block;
text-align: center;
text-decoration: none;
}
#vt100 #keyboard b, #vt100 #keyboard s {
width: 2ex;
}
#vt100 #keyboard u, #vt100 #keyboard s {
visibility: hidden;
}
#vt100 #keyboard .shifted {
display: none;
}
#vt100 #keyboard .selected {
color: #888888;
background-color: #eeeeee;
box-shadow: 0px 0px 3px #222222;
-webkit-box-shadow: 0px 0px 3px #222222;
position: relative;
top: 1px;
left: 1px;
}
[if DEFINES_COLORS] [if DEFINES_COLORS]
/* IE cannot properly handle "inherit" properties. So, the monochrome.css/ /* IE cannot properly handle "inherit" properties. So, the monochrome.css/
* color.css style sheets cannot work, if we define colors in styles.css. * color.css style sheets cannot work, if we define colors in styles.css.
@ -177,19 +254,19 @@
@media print { @media print {
#vt100 .scrollback { #vt100 .scrollback {
display: none; display: none;
} }
#vt100 #reconnect, #vt100 #cursor, #vt100 #menu { #vt100 #reconnect, #vt100 #cursor, #vt100 #menu, #vt100 #kbd_button, #vt100 #keyboard {
visibility: hidden; visibility: hidden;
} }
#vt100 #scrollable { #vt100 #scrollable {
overflow: hidden; overflow: hidden;
} }
#vt100 #console, #vt100 #alt_console { #vt100 #console, #vt100 #alt_console {
overflow: hidden; overflow: hidden;
width: 1000000ex; width: 1000000ex;
} }
} }

View file

@ -238,22 +238,16 @@ VT100.prototype.reset = function(clearHistory) {
this.enableAlternateScreen(false); this.enableAlternateScreen(false);
var wasCompressed = false; var wasCompressed = false;
var styles = [ 'transform', var transform = this.getTransformName();
'WebkitTransform', if (transform) {
'MozTransform', for (var i = 0; i < 2; ++i) {
'filter' ]; wasCompressed |= this.console[i].style[transform] != '';
for (var i = 0; i < styles.length; ++i) { this.console[i].style[transform] = '';
if (typeof this.console[0].style[styles[i]] != 'undefined') { }
for (var j = 0; j < 1; ++j) { this.cursor.style[transform] = '';
wasCompressed |= this.console[j].style[styles[i]] != ''; this.space.style[transform] = '';
this.console[j].style[styles[i]] = ''; if (transform == 'filter') {
} this.console[this.currentScreen].style.width = '';
this.cursor.style[styles[i]] = '';
this.space.style[styles[i]] = '';
if (styles[i] == 'filter') {
this.console[this.currentScreen].style.width = '';
}
break;
} }
} }
this.scale = 1.0; this.scale = 1.0;
@ -270,10 +264,13 @@ VT100.prototype.reset = function(clearHistory) {
}; };
VT100.prototype.addListener = function(elem, event, listener) { VT100.prototype.addListener = function(elem, event, listener) {
if (elem.addEventListener) { try {
elem.addEventListener(event, listener, false); if (elem.addEventListener) {
} else { elem.addEventListener(event, listener, false);
elem.attachEvent('on' + event, listener); } else {
elem.attachEvent('on' + event, listener);
}
} catch (e) {
} }
}; };
@ -281,11 +278,12 @@ VT100.prototype.getUserSettings = function() {
// Compute hash signature to identify the entries in the userCSS menu. // Compute hash signature to identify the entries in the userCSS menu.
// If the menu is unchanged from last time, default values can be // If the menu is unchanged from last time, default values can be
// looked up in a cookie associated with this page. // looked up in a cookie associated with this page.
this.signature = 2; this.signature = 3;
this.utfPreferred = true; this.utfPreferred = true;
this.visualBell = typeof suppressAllAudio != 'undefined' && this.visualBell = typeof suppressAllAudio != 'undefined' &&
suppressAllAudio; suppressAllAudio;
this.autoprint = true; this.autoprint = true;
this.softKeyboard = false;
this.blinkingCursor = true; this.blinkingCursor = true;
if (this.visualBell) { if (this.visualBell) {
this.signature = Math.floor(16807*this.signature + 1) % this.signature = Math.floor(16807*this.signature + 1) %
@ -311,15 +309,16 @@ VT100.prototype.getUserSettings = function() {
if (settings >= 0) { if (settings >= 0) {
settings = document.cookie.substr(settings + key.length). settings = document.cookie.substr(settings + key.length).
replace(/([0-1]*).*/, "$1"); replace(/([0-1]*).*/, "$1");
if (settings.length == 3 + (typeof userCSSList == 'undefined' ? if (settings.length == 5 + (typeof userCSSList == 'undefined' ?
0 : userCSSList.length)) { 0 : userCSSList.length)) {
this.utfPreferred = settings.charAt(0) != '0'; this.utfPreferred = settings.charAt(0) != '0';
this.visualBell = settings.charAt(1) != '0'; this.visualBell = settings.charAt(1) != '0';
this.autoprint = settings.charAt(2) != '0'; this.autoprint = settings.charAt(2) != '0';
this.blinkingCursor = settings.charAt(3) != '0'; this.softKeyboard = settings.charAt(3) != '0';
this.blinkingCursor = settings.charAt(4) != '0';
if (typeof userCSSList != 'undefined') { if (typeof userCSSList != 'undefined') {
for (var i = 0; i < userCSSList.length; ++i) { for (var i = 0; i < userCSSList.length; ++i) {
userCSSList[i][2] = settings.charAt(i + 3) != '0'; userCSSList[i][2] = settings.charAt(i + 5) != '0';
} }
} }
} }
@ -332,6 +331,7 @@ VT100.prototype.storeUserSettings = function() {
(this.utfEnabled ? '1' : '0') + (this.utfEnabled ? '1' : '0') +
(this.visualBell ? '1' : '0') + (this.visualBell ? '1' : '0') +
(this.autoprint ? '1' : '0') + (this.autoprint ? '1' : '0') +
(this.softKeyboard ? '1' : '0') +
(this.blinkingCursor ? '1' : '0'); (this.blinkingCursor ? '1' : '0');
if (typeof userCSSList != 'undefined') { if (typeof userCSSList != 'undefined') {
for (var i = 0; i < userCSSList.length; ++i) { for (var i = 0; i < userCSSList.length; ++i) {
@ -413,7 +413,7 @@ VT100.prototype.initializeUserCSSStyles = function() {
label.textContent= label.textContent; label.textContent= label.textContent;
} }
// User style sheets are number sequentially // User style sheets are numbered sequentially
var sheet = document.getElementById( var sheet = document.getElementById(
'usercss-' + i); 'usercss-' + i);
if (i == current) { if (i == current) {
@ -470,6 +470,328 @@ VT100.prototype.initializeUserCSSStyles = function() {
} }
}; };
VT100.prototype.resetLastSelectedKey = function(e) {
var key = this.lastSelectedKey;
if (!key) {
return false;
}
var position = this.mousePosition(e);
// We don't get all the necessary events to reliably reselect a key
// if we moved away from it and then back onto it. We approximate the
// behavior by remembering the key until either we release the mouse
// button (we might never get this event if the mouse has since left
// the window), or until we move away too far.
var box = this.keyboard.firstChild;
if (position[0] < box.offsetLeft + key.offsetWidth ||
position[1] < box.offsetTop + key.offsetHeight ||
position[0] >= box.offsetLeft + box.offsetWidth - key.offsetWidth ||
position[1] >= box.offsetTop + box.offsetHeight - key.offsetHeight ||
position[0] < box.offsetLeft + key.offsetLeft - key.offsetWidth ||
position[1] < box.offsetTop + key.offsetTop - key.offsetHeight ||
position[0] >= box.offsetLeft + key.offsetLeft + 2*key.offsetWidth ||
position[1] >= box.offsetTop + key.offsetTop + 2*key.offsetHeight) {
if (this.lastSelectedKey.className) log.console('reset: deselecting');
this.lastSelectedKey.className = '';
this.lastSelectedKey = undefined;
}
return false;
};
VT100.prototype.showShiftState = function(state) {
var style = document.getElementById('shift_state');
if (state) {
this.setTextContentRaw(style,
'#vt100 #keyboard .shifted {' +
'display: inline }' +
'#vt100 #keyboard .unshifted {' +
'display: none }');
} else {
this.setTextContentRaw(style, '');
}
var elems = this.keyboard.getElementsByTagName('I');
for (var i = 0; i < elems.length; ++i) {
if (elems[i].id == '16') {
elems[i].className = state ? 'selected' : '';
}
}
};
VT100.prototype.showCtrlState = function(state) {
var ctrl = this.getChildById(this.keyboard, '17' /* Ctrl */);
if (ctrl) {
ctrl.className = state ? 'selected' : '';
}
};
VT100.prototype.showAltState = function(state) {
var alt = this.getChildById(this.keyboard, '18' /* Alt */);
if (alt) {
alt.className = state ? 'selected' : '';
}
};
VT100.prototype.clickedKeyboard = function(e, elem, ch, key, shift, ctrl, alt){
var fake = [ ];
fake.charCode = ch;
fake.keyCode = key;
fake.ctrlKey = ctrl;
fake.shiftKey = shift;
fake.altKey = alt;
fake.metaKey = alt;
return this.handleKey(fake);
};
VT100.prototype.addKeyBinding = function(elem, ch, key, CH, KEY) {
if (elem == undefined) {
return;
}
if (ch == '\u00A0') {
// &nbsp; should be treated as a regular space character.
ch = ' ';
}
if (ch != undefined && CH == undefined) {
// For letter keys, we automatically compute the uppercase character code
// from the lowercase one.
CH = ch.toUpperCase();
}
if (KEY == undefined && key != undefined) {
// Most keys have identically key codes for both lowercase and uppercase
// keypresses. Normally, only function keys would have distinct key codes,
// whereas regular keys have character codes.
KEY = key;
} else if (KEY == undefined && CH != undefined) {
// For regular keys, copy the character code to the key code.
KEY = CH.charCodeAt(0);
}
if (key == undefined && ch != undefined) {
// For regular keys, copy the character code to the key code.
key = ch.charCodeAt(0);
}
// Convert characters to numeric character codes. If the character code
// is undefined (i.e. this is a function key), set it to zero.
ch = ch ? ch.charCodeAt(0) : 0;
CH = CH ? CH.charCodeAt(0) : 0;
// Mouse down events high light the key. We also set lastSelectedKey. This
// is needed to that mouseout/mouseover can keep track of the key that
// is currently being clicked.
this.addListener(elem, 'mousedown',
function(vt100, elem, key) { return function(e) {
if ((e.which || e.button) == 1) {
if (vt100.lastSelectedKey) {
vt100.lastSelectedKey.className= '';
}
// Highlight the key while the mouse button is held down.
if (key == 16 /* Shift */) {
if (!elem.className != vt100.isShift) {
vt100.showShiftState(!vt100.isShift);
}
} else if (key == 17 /* Ctrl */) {
if (!elem.className != vt100.isCtrl) {
vt100.showCtrlState(!vt100.isCtrl);
}
} else if (key == 18 /* Alt */) {
if (!elem.className != vt100.isAlt) {
vt100.showAltState(!vt100.isAlt);
}
} else {
elem.className = 'selected';
}
vt100.lastSelectedKey = elem;
}
return false; }; }(this, elem, key));
var clicked =
// Modifier keys update the state of the keyboard, but do not generate
// any key clicks that get forwarded to the application.
key >= 16 /* Shift */ && key <= 18 /* Alt */ ?
function(vt100, elem) { return function(e) {
if (elem == vt100.lastSelectedKey) {
if (key == 16 /* Shift */) {
// The user clicked the Shift key
vt100.isShift = !vt100.isShift;
vt100.showShiftState(vt100.isShift);
} else if (key == 17 /* Ctrl */) {
vt100.isCtrl = !vt100.isCtrl;
vt100.showCtrlState(vt100.isCtrl);
} else if (key == 18 /* Alt */) {
vt100.isAlt = !vt100.isAlt;
vt100.showAltState(vt100.isAlt);
}
vt100.lastSelectedKey = undefined;
}
if (vt100.lastSelectedKey) {
vt100.lastSelectedKey.className = '';
vt100.lastSelectedKey = undefined;
}
return false; }; }(this, elem) :
// Regular keys generate key clicks, when the mouse button is released or
// when a mouse click event is received.
function(vt100, elem, ch, key, CH, KEY) { return function(e) {
if (vt100.lastSelectedKey) {
if (elem == vt100.lastSelectedKey) {
// The user clicked a key.
if (vt100.isShift) {
vt100.clickedKeyboard(e, elem, CH, KEY,
true, vt100.isCtrl, vt100.isAlt);
} else {
vt100.clickedKeyboard(e, elem, ch, key,
false, vt100.isCtrl, vt100.isAlt);
}
vt100.isShift = false;
vt100.showShiftState(false);
vt100.isCtrl = false;
vt100.showCtrlState(false);
vt100.isAlt = false;
vt100.showAltState(false);
}
vt100.lastSelectedKey.className = '';
vt100.lastSelectedKey = undefined;
}
elem.className = '';
return false; }; }(this, elem, ch, key, CH, KEY);
this.addListener(elem, 'mouseup', clicked);
this.addListener(elem, 'click', clicked);
// When moving the mouse away from a key, check if any keys need to be
// deselected.
this.addListener(elem, 'mouseout',
function(vt100, elem, key) { return function(e) {
if (key == 16 /* Shift */) {
if (!elem.className == vt100.isShift) {
vt100.showShiftState(vt100.isShift);
}
} else if (key == 17 /* Ctrl */) {
if (!elem.className == vt100.isCtrl) {
vt100.showCtrlState(vt100.isCtrl);
}
} else if (key == 18 /* Alt */) {
if (!elem.className == vt100.isAlt) {
vt100.showAltState(vt100.isAlt);
}
} else if (elem.className) {
elem.className = '';
vt100.lastSelectedKey = elem;
} else if (vt100.lastSelectedKey) {
vt100.resetLastSelectedKey(e);
}
return false; }; }(this, elem, key));
// When moving the mouse over a key, select it if the user is still holding
// the mouse button down (i.e. elem == lastSelectedKey)
this.addListener(elem, 'mouseover',
function(vt100, elem, key) { return function(e) {
if (elem == vt100.lastSelectedKey) {
if (key == 16 /* Shift */) {
if (!elem.className != vt100.isShift) {
vt100.showShiftState(!vt100.isShift);
}
} else if (key == 17 /* Ctrl */) {
if (!elem.className != vt100.isCtrl) {
vt100.showCtrlState(!vt100.isCtrl);
}
} else if (key == 18 /* Alt */) {
if (!elem.className != vt100.isAlt) {
vt100.showAltState(!vt100.isAlt);
}
} else if (!elem.className) {
elem.className = 'selected';
}
} else {
vt100.resetLastSelectedKey(e);
}
return false; }; }(this, elem, key));
};
VT100.prototype.initializeKeyBindings = function(elem) {
if (elem) {
if (elem.nodeName == "I" || elem.nodeName == "B") {
if (elem.id) {
// Function keys. The Javascript keycode is part of the "id"
var i = parseInt(elem.id);
if (i) {
// If the id does not parse as a number, it is not a keycode.
this.addKeyBinding(elem, undefined, i);
}
} else {
var child = elem.firstChild;
if (child.nodeName == "#text") {
// If the key only has a text node as a child, then it is a letter.
// Automatically compute the lower and upper case version of the key.
this.addKeyBinding(elem, this.getTextContent(child).toLowerCase());
} else {
// If the key has two children, they are the lower and upper case
// character code, respectively.
this.addKeyBinding(elem, this.getTextContent(child), undefined,
this.getTextContent(child.nextSibling));
}
}
}
}
// Recursively parse all other child nodes.
for (elem = elem.firstChild; elem; elem = elem.nextSibling) {
this.initializeKeyBindings(elem);
}
};
VT100.prototype.initializeKeyboard = function() {
// Configure mouse event handlers for button that displays/hides keyboard
var box = this.keyboard.firstChild;
this.hideSoftKeyboard();
this.addListener(this.keyboardImage, 'click',
function(vt100) { return function(e) {
if (vt100.keyboard.style.display != '') {
if (vt100.reconnectBtn.style.visibility != '') {
vt100.showSoftKeyboard();
}
} else {
vt100.hideSoftKeyboard();
vt100.input.focus();
}
return false; }; }(this));
// Enable button that displays keyboard
if (this.softKeyboard) {
this.keyboardImage.style.visibility = 'visible';
}
// Configure mouse event handlers for on-screen keyboard
this.addListener(this.keyboard, 'click',
function(vt100) { return function(e) {
vt100.hideSoftKeyboard();
vt100.input.focus();
return false; }; }(this));
this.addListener(this.keyboard, 'selectstart', this.cancelEvent);
this.addListener(box, 'click', this.cancelEvent);
this.addListener(box, 'mouseup',
function(vt100) { return function(e) {
if (vt100.lastSelectedKey) {
vt100.lastSelectedKey.className = '';
vt100.lastSelectedKey = undefined;
}
return false; }; }(this));
this.addListener(box, 'mouseout',
function(vt100) { return function(e) {
return vt100.resetLastSelectedKey(e); }; }(this));
this.addListener(box, 'mouseover',
function(vt100) { return function(e) {
return vt100.resetLastSelectedKey(e); }; }(this));
// Configure SHIFT key behavior
var style = document.createElement('style');
var id = document.createAttribute('id');
id.nodeValue = 'shift_state';
style.setAttributeNode(id);
var type = document.createAttribute('type');
type.nodeValue = 'text/css';
style.setAttributeNode(type);
document.getElementsByTagName('head')[0].appendChild(style);
// Set up key bindings
this.initializeKeyBindings(box);
};
VT100.prototype.initializeElements = function(container) { VT100.prototype.initializeElements = function(container) {
// If the necessary objects have not already been defined in the HTML // If the necessary objects have not already been defined in the HTML
// page, create them now. // page, create them now.
@ -483,6 +805,9 @@ VT100.prototype.initializeElements = function(container) {
if (!this.getChildById(this.container, 'reconnect') || if (!this.getChildById(this.container, 'reconnect') ||
!this.getChildById(this.container, 'menu') || !this.getChildById(this.container, 'menu') ||
!this.getChildById(this.container, 'keyboard') ||
!this.getChildById(this.container, 'kbd_button') ||
!this.getChildById(this.container, 'kbd_img') ||
!this.getChildById(this.container, 'scrollable') || !this.getChildById(this.container, 'scrollable') ||
!this.getChildById(this.container, 'console') || !this.getChildById(this.container, 'console') ||
!this.getChildById(this.container, 'alt_console') || !this.getChildById(this.container, 'alt_console') ||
@ -525,7 +850,15 @@ VT100.prototype.initializeElements = function(container) {
'<div id="cursize" style="visibility: hidden">' + '<div id="cursize" style="visibility: hidden">' +
'</div>' + '</div>' +
'<div id="menu"></div>' + '<div id="menu"></div>' +
'<div id="keyboard" unselectable="on">' +
'<pre class="box"><div><i id="27">Esc</i><i id="112">F1</i><i id="113">F2</i><i id="114">F3</i><i id="115">F4</i><i id="116">F5</i><i id="117">F6</i><i id="118">F7</i><i id="119">F8</i><i id="120">F9</i><i id="121">F10</i><i id="122">F11</i><i id="123">F12</i><br /><b><span class="unshifted">`</span><span class="shifted">~</span></b><b><span class="unshifted">1</span><span class="shifted">!</span></b><b><span class="unshifted">2</span><span class="shifted">@</span></b><b><span class="unshifted">3</span><span class="shifted">#</span></b><b><span class="unshifted">4</span><span class="shifted">&#36;</span></b><b><span class="unshifted">5</span><span class="shifted">&#37;</span></b><b><span class="unshifted">6</span><span class="shifted">^</span></b><b><span class="unshifted">7</span><span class="shifted">&amp;</span></b><b><span class="unshifted">8</span><span class="shifted">*</span></b><b><span class="unshifted">9</span><span class="shifted">(</span></b><b><span class="unshifted">0</span><span class="shifted">)</span></b><b><span class="unshifted">-</span><span class="shifted">_</span></b><b><span class="unshifted">=</span><span class="shifted">+</span></b><i id="8">&nbsp;&larr;&nbsp;</i><br /><i id="9">Tab</i><b>Q</b><b>W</b><b>E</b><b>R</b><b>T</b><b>Y</b><b>U</b><b>I</b><b>O</b><b>P</b><b><span class="unshifted">[</span><span class="shifted">{</span></b><b><span class="unshifted">]</span><span class="shifted">}</span></b><b><span class="unshifted">&#92;</span><span class="shifted">|</span></b><br /><u>Tab&nbsp;&nbsp;</u><b>A</b><b>S</b><b>D</b><b>F</b><b>G</b><b>H</b><b>J</b><b>K</b><b>L</b><b><span class="unshifted">;</span><span class="shifted">:</span></b><b><span class="unshifted">&#39;</span><span class="shifted">"</span></b><i id="13">Enter</i><br /><u>&nbsp;&nbsp;</u><i id="16">Shift</i><b>Z</b><b>X</b><b>C</b><b>V</b><b>B</b><b>N</b><b>M</b><b><span class="unshifted">,</span><span class="shifted">&lt;</span></b><b><span class="unshifted">.</span><span class="shifted">&gt;</span></b><b><span class="unshifted">/</span><span class="shifted">?</span></b><i id="16">Shift</i><br /><u>XXX</u><i id="17">Ctrl</i><i id="18">Alt</i><i style="width: 25ex">&nbsp</i></div>&nbsp;&nbsp;&nbsp;<div><i id="45">Ins</i><i id="46">Del</i><i id="36">Home</i><i id="35">End</i><br /><u>&nbsp;</u><br /><u>&nbsp;</u><br /><u>Ins</u><s>&nbsp;</s><b id="38">&uarr;</b><s>&nbsp;</s><u>&nbsp;</u><b id="33">&uArr;</b><br /><u>Ins</u><b id="37">&larr;</b><b id="40">&darr;</b><b id="39">&rarr;</b><u>&nbsp;</u><b id="34">&dArr;</b></div></pre>' +
'</div>' +
'<div id="scrollable">' + '<div id="scrollable">' +
'<table id="kbd_button">' +
'<tr><td width="100%">&nbsp;</td>' +
'<td><img id="kbd_img" src="keyboard.png" /></td>' +
'<td>&nbsp;&nbsp;&nbsp;&nbsp;</td></tr>' +
'</table>' +
'<pre id="lineheight">&nbsp;</pre>' + '<pre id="lineheight">&nbsp;</pre>' +
'<pre id="console">' + '<pre id="console">' +
'<pre></pre>' + '<pre></pre>' +
@ -566,6 +899,8 @@ VT100.prototype.initializeElements = function(container) {
this.reconnectBtn = this.getChildById(this.container,'reconnect'); this.reconnectBtn = this.getChildById(this.container,'reconnect');
this.curSizeBox = this.getChildById(this.container, 'cursize'); this.curSizeBox = this.getChildById(this.container, 'cursize');
this.menu = this.getChildById(this.container, 'menu'); this.menu = this.getChildById(this.container, 'menu');
this.keyboard = this.getChildById(this.container, 'keyboard');
this.keyboardImage = this.getChildById(this.container, 'kbd_img');
this.scrollable = this.getChildById(this.container, this.scrollable = this.getChildById(this.container,
'scrollable'); 'scrollable');
this.lineheight = this.getChildById(this.container, this.lineheight = this.getChildById(this.container,
@ -646,6 +981,9 @@ VT100.prototype.initializeElements = function(container) {
// Hide context menu // Hide context menu
this.hideContextMenu(); this.hideContextMenu();
// Set up onscreen soft keyboard
this.initializeKeyboard();
// Add listener to reconnect button // Add listener to reconnect button
this.addListener(this.reconnectBtn.firstChild, 'click', this.addListener(this.reconnectBtn.firstChild, 'click',
function(vt100) { function(vt100) {
@ -733,6 +1071,7 @@ VT100.prototype.reconnect = function() {
VT100.prototype.showReconnect = function(state) { VT100.prototype.showReconnect = function(state) {
if (state) { if (state) {
this.hideSoftKeyboard();
this.reconnectBtn.style.visibility = ''; this.reconnectBtn.style.visibility = '';
} else { } else {
this.reconnectBtn.style.visibility = 'hidden'; this.reconnectBtn.style.visibility = 'hidden';
@ -766,6 +1105,9 @@ VT100.prototype.resized = function(w, h) {
}; };
VT100.prototype.resizer = function() { VT100.prototype.resizer = function() {
// Hide onscreen soft keyboard
this.hideSoftKeyboard();
// The cursor can get corrupted if the print-preview is displayed in Firefox. // The cursor can get corrupted if the print-preview is displayed in Firefox.
// Recreating it, will repair it. // Recreating it, will repair it.
var newCursor = document.createElement('pre'); var newCursor = document.createElement('pre');
@ -945,6 +1287,17 @@ VT100.prototype.cancelEvent = function(event) {
return false; return false;
}; };
VT100.prototype.mousePosition = function(event) {
var offsetX = this.container.offsetLeft;
var offsetY = this.container.offsetTop;
for (var e = this.container; e = e.offsetParent; ) {
offsetX += e.offsetLeft;
offsetY += e.offsetTop;
}
return [ event.clientX - offsetX,
event.clientY - offsetY ];
};
VT100.prototype.mouseEvent = function(event, type) { VT100.prototype.mouseEvent = function(event, type) {
// If any text is currently selected, do not move the focus as that would // If any text is currently selected, do not move the focus as that would
// invalidate the selection. // invalidate the selection.
@ -954,15 +1307,10 @@ VT100.prototype.mouseEvent = function(event, type) {
} }
// Compute mouse position in characters. // Compute mouse position in characters.
var offsetX = this.container.offsetLeft; var position = this.mousePosition(event);
var offsetY = this.container.offsetTop; var x = Math.floor(position[0] / this.cursorWidth);
for (var e = this.container; e = e.offsetParent; ) { var y = Math.floor((position[1] + this.scrollable.scrollTop) /
offsetX += e.offsetLeft; this.cursorHeight) - this.numScrollbackLines;
offsetY += e.offsetTop;
}
var x = (event.clientX - offsetX) / this.cursorWidth;
var y = ((event.clientY - offsetY) + this.scrollable.offsetTop) /
this.cursorHeight - this.numScrollbackLines;
var inside = true; var inside = true;
if (x >= this.terminalWidth) { if (x >= this.terminalWidth) {
x = this.terminalWidth - 1; x = this.terminalWidth - 1;
@ -1022,7 +1370,7 @@ VT100.prototype.mouseEvent = function(event, type) {
// Bring up context menu. // Bring up context menu.
if (button == 2 && !event.shiftKey) { if (button == 2 && !event.shiftKey) {
if (type == 0 /* MOUSE_DOWN */) { if (type == 0 /* MOUSE_DOWN */) {
this.showContextMenu(event.clientX - offsetX, event.clientY - offsetY); this.showContextMenu(position[0], position[1]);
} }
return this.cancelEvent(event); return this.cancelEvent(event);
} }
@ -1058,6 +1406,29 @@ VT100.prototype.getTextContent = function(elem) {
(typeof elem.textContent == 'undefined' ? elem.innerText : ''); (typeof elem.textContent == 'undefined' ? elem.innerText : '');
}; };
VT100.prototype.setTextContentRaw = function(elem, s) {
// Updating the content of an element is an expensive operation. It actually
// pays off to first check whether the element is still unchanged.
if (typeof elem.textContent == 'undefined') {
if (elem.innerText != s) {
try {
elem.innerText = s;
} catch (e) {
// Very old versions of IE do not allow setting innerText. Instead,
// remove all children, by setting innerHTML and then set the text
// using DOM methods.
elem.innerHTML = '';
elem.appendChild(document.createTextNode(
this.replaceChar(s, ' ', '\u00A0')));
}
}
} else {
if (elem.textContent != s) {
elem.textContent = s;
}
}
};
VT100.prototype.setTextContent = function(elem, s) { VT100.prototype.setTextContent = function(elem, s) {
// Check if we find any URLs in the text. If so, automatically convert them // Check if we find any URLs in the text. If so, automatically convert them
// to links. // to links.
@ -1103,26 +1474,7 @@ VT100.prototype.setTextContent = function(elem, s) {
return; return;
} }
// Updating the content of an element is an expensive operation. It actually this.setTextContentRaw(elem, s);
// pays off to first check whether the element is still unchanged.
if (typeof elem.textContent == 'undefined') {
if (elem.innerText != s) {
try {
elem.innerText = s;
} catch (e) {
// Very old versions of IE do not allow setting innerText. Instead,
// remove all children, by setting innerHTML and then set the text
// using DOM methods.
elem.innerHTML = '';
elem.appendChild(document.createTextNode(
this.replaceChar(s, ' ', '\u00A0')));
}
}
} else {
if (elem.textContent != s) {
elem.textContent = s;
}
}
}; };
VT100.prototype.insertBlankLine = function(y, color, style) { VT100.prototype.insertBlankLine = function(y, color, style) {
@ -1578,27 +1930,21 @@ VT100.prototype.enableAlternateScreen = function(state) {
this.console[this.currentScreen].style.display = ''; this.console[this.currentScreen].style.display = '';
// Select appropriate character pitch. // Select appropriate character pitch.
var styles = [ 'transform', var transform = this.getTransformName();
'WebkitTransform', if (transform) {
'MozTransform', if (state) {
'filter' ]; // Upon enabling the alternate screen, we switch to 80 column mode. But
for (var i = 0; i < styles.length; ++i) { // upon returning to the regular screen, we restore the mode that was
if (typeof this.console[0].style[styles[i]] != 'undefined') { // in effect previously.
if (state) { this.console[1].style[transform] = '';
// Upon enabling the alternate screen, we switch to 80 column mode. But }
// upon returning to the regular screen, we restore the mode that was var style =
// in effect previously. this.console[this.currentScreen].style[transform];
this.console[1].style[styles[i]] = ''; this.cursor.style[transform] = style;
} this.space.style[transform] = style;
var style = this.scale = style == '' ? 1.0:1.65;
this.console[this.currentScreen].style[styles[i]]; if (transform == 'filter') {
this.cursor.style[styles[i]] = style; this.console[this.currentScreen].style.width = style == '' ? '165%':'';
this.space.style[styles[i]] = style;
this.scale = style == '' ? 1.0:1.65;
if (styles[i] == 'filter') {
this.console[this.currentScreen].style.width = style == '' ? '165%':'';
}
break;
} }
} }
this.resizer(); this.resizer();
@ -1969,12 +2315,76 @@ VT100.prototype.toggleBell = function() {
this.visualBell = !this.visualBell; this.visualBell = !this.visualBell;
}; };
VT100.prototype.toggleSoftKeyboard = function() {
this.softKeyboard = !this.softKeyboard;
this.keyboardImage.style.visibility = this.softKeyboard ? 'visible' : '';
};
VT100.prototype.deselectKeys = function(elem) {
if (elem && elem.className == 'selected') {
elem.className = '';
}
for (elem = elem.firstChild; elem; elem = elem.nextSibling) {
this.deselectKeys(elem);
}
};
VT100.prototype.showSoftKeyboard = function() {
// Make sure no key is currently selected
this.lastSelectedKey = undefined;
this.deselectKeys(this.keyboard);
this.isShift = false;
this.showShiftState(false);
this.isCtrl = false;
this.showCtrlState(false);
this.isAlt = false;
this.showAltState(false);
this.keyboard.style.left = '0px';
this.keyboard.style.top = '0px';
this.keyboard.style.width = this.container.offsetWidth + 'px';
this.keyboard.style.height = this.container.offsetHeight + 'px';
this.keyboard.style.visibility = 'hidden';
this.keyboard.style.display = '';
var kbd = this.keyboard.firstChild;
var scale = 1.0;
var transform = this.getTransformName();
if (transform) {
kbd.style[transform] = '';
if (kbd.offsetWidth > 0.9 * this.container.offsetWidth) {
scale = (kbd.offsetWidth/
this.container.offsetWidth)/0.9;
}
if (kbd.offsetHeight > 0.9 * this.container.offsetHeight) {
scale = Math.max((kbd.offsetHeight/
this.container.offsetHeight)/0.9);
}
var style = this.getTransformStyle(transform,
scale > 1.0 ? scale : undefined);
kbd.style[transform] = style;
}
if (transform == 'filter') {
scale = 1.0;
}
kbd.style.left = ((this.container.offsetWidth -
kbd.offsetWidth/scale)/2) + 'px';
kbd.style.top = ((this.container.offsetHeight -
kbd.offsetHeight/scale)/2) + 'px';
this.keyboard.style.visibility = 'visible';
};
VT100.prototype.hideSoftKeyboard = function() {
this.keyboard.style.display = 'none';
};
VT100.prototype.toggleCursorBlinking = function() { VT100.prototype.toggleCursorBlinking = function() {
this.blinkingCursor = !this.blinkingCursor; this.blinkingCursor = !this.blinkingCursor;
}; };
VT100.prototype.about = function() { VT100.prototype.about = function() {
alert("VT100 Terminal Emulator " + "2.10 (revision 220)" + alert("VT100 Terminal Emulator " + "2.10 (revision 221)" +
"\nCopyright 2008-2010 by Markus Gutschke\n" + "\nCopyright 2008-2010 by Markus Gutschke\n" +
"For more information check http://shellinabox.com"); "For more information check http://shellinabox.com");
}; };
@ -2007,6 +2417,9 @@ VT100.prototype.showContextMenu = function(x, y) {
'<li>' + '<li>' +
(this.visualBell ? '<img src="enabled.gif" />' : '') + (this.visualBell ? '<img src="enabled.gif" />' : '') +
'Visual Bell</li>'+ 'Visual Bell</li>'+
'<li>' +
(this.softKeyboard ? '<img src="enabled.gif" />' : '') +
'Onscreen Keyboard</li>' +
'<li id="endconfig">' + '<li id="endconfig">' +
(this.blinkingCursor ? '<img src="enabled.gif" />' : '') + (this.blinkingCursor ? '<img src="enabled.gif" />' : '') +
'Blinking Cursor</li>'+ 'Blinking Cursor</li>'+
@ -2038,6 +2451,7 @@ VT100.prototype.showContextMenu = function(x, y) {
// Actions for default items // Actions for default items
var actions = [ this.copyLast, p, this.reset, var actions = [ this.copyLast, p, this.reset,
this.toggleUTF, this.toggleBell, this.toggleUTF, this.toggleBell,
this.toggleSoftKeyboard,
this.toggleCursorBlinking ]; this.toggleCursorBlinking ];
// Actions for user CSS styles (if any) // Actions for user CSS styles (if any)
@ -2093,26 +2507,30 @@ VT100.prototype.showContextMenu = function(x, y) {
} }
// Position menu next to the mouse pointer // Position menu next to the mouse pointer
if (x + popup.clientWidth > this.container.offsetWidth) { this.menu.style.left = '0px';
x = this.container.offsetWidth - popup.clientWidth; this.menu.style.top = '0px';
this.menu.style.width = this.container.offsetWidth + 'px';
this.menu.style.height = this.container.offsetHeight + 'px';
popup.style.left = '0px';
popup.style.top = '0px';
var margin = 2;
if (x + popup.clientWidth >= this.container.offsetWidth - margin) {
x = this.container.offsetWidth-popup.clientWidth - margin - 1;
} }
if (x < 0) { if (x < margin) {
x = 0; x = margin;
} }
if (y + popup.clientHeight > this.container.offsetHeight) { if (y + popup.clientHeight >= this.container.offsetHeight - margin) {
y = this.container.offsetHeight-popup.clientHeight; y = this.container.offsetHeight-popup.clientHeight - margin - 1;
} }
if (y < 0) { if (y < margin) {
y = 0; y = margin;
} }
popup.style.left = x + 'px'; popup.style.left = x + 'px';
popup.style.top = y + 'px'; popup.style.top = y + 'px';
// Block all other interactions with the terminal emulator // Block all other interactions with the terminal emulator
this.menu.style.left = '0px';
this.menu.style.top = '0px';
this.menu.style.width = this.container.offsetWidth + 'px';
this.menu.style.height = this.container.offsetHeight + 'px';
this.addListener(this.menu, 'click', function(vt100) { this.addListener(this.menu, 'click', function(vt100) {
return function() { return function() {
vt100.hideContextMenu(); vt100.hideContextMenu();
@ -2895,39 +3313,42 @@ VT100.prototype.restoreCursor = function() {
this.savedY[this.currentScreen]); this.savedY[this.currentScreen]);
}; };
VT100.prototype.set80_132Mode = function(state) { VT100.prototype.getTransformName = function() {
var transform = undefined; var styles = [ 'transform', 'WebkitTransform', 'MozTransform', 'filter' ];
var styles = [ 'transform',
'WebkitTransform',
'MozTransform',
'filter'
];
for (var i = 0; i < styles.length; ++i) { for (var i = 0; i < styles.length; ++i) {
if (typeof this.console[0].style[styles[i]] != 'undefined') { if (typeof this.console[0].style[styles[i]] != 'undefined') {
transform = styles[i]; return styles[i];
break;
} }
} }
return undefined;
};
VT100.prototype.getTransformStyle = function(transform, scale) {
return scale && scale != 1.0
? transform == 'filter'
? 'progid:DXImageTransform.Microsoft.Matrix(' +
'M11=' + (1.0/scale) + ',M12=0,M21=0,M22=1,' +
"sizingMethod='auto expand')"
: 'translateX(-50%) ' +
'scaleX(' + (1.0/scale) + ') ' +
'translateX(50%)'
: '';
};
VT100.prototype.set80_132Mode = function(state) {
var transform = this.getTransformName();
if (transform) { if (transform) {
if ((this.console[this.currentScreen].style[transform] != '') == state) { if ((this.console[this.currentScreen].style[transform] != '') == state) {
return; return;
} }
var style = var style = state ?
state ? transform == 'filter' this.getTransformStyle(transform, 1.65):'';
? 'progid:DXImageTransform.Microsoft.Matrix(' +
'M11=0.606060606060606060606,M12=0,M21=0,M22=1,' +
"sizingMethod='auto expand')"
: 'translateX(-50%) ' +
'scaleX(0.606060606060606060606) ' +
'translateX(50%)'
: '';
this.console[this.currentScreen].style[transform] = style; this.console[this.currentScreen].style[transform] = style;
this.cursor.style[transform] = style; this.cursor.style[transform] = style;
this.space.style[transform] = style; this.space.style[transform] = style;
this.scale = state ? 1.65 : 1.0; this.scale = state ? 1.65 : 1.0;
if (transform == 'filter') { if (transform == 'filter') {
this.console[this.currentScreen].style.width = state ? '165%' : ''; this.console[this.currentScreen].style.width = state ? '165%' : '';
} }
this.resizer(); this.resizer();
} }

View file

@ -238,22 +238,16 @@ VT100.prototype.reset = function(clearHistory) {
this.enableAlternateScreen(false); this.enableAlternateScreen(false);
var wasCompressed = false; var wasCompressed = false;
var styles = [ 'transform', var transform = this.getTransformName();
'WebkitTransform', if (transform) {
'MozTransform', for (var i = 0; i < 2; ++i) {
'filter' ]; wasCompressed |= this.console[i].style[transform] != '';
for (var i = 0; i < styles.length; ++i) { this.console[i].style[transform] = '';
if (typeof this.console[0].style[styles[i]] != 'undefined') { }
for (var j = 0; j < 1; ++j) { this.cursor.style[transform] = '';
wasCompressed |= this.console[j].style[styles[i]] != ''; this.space.style[transform] = '';
this.console[j].style[styles[i]] = ''; if (transform == 'filter') {
} this.console[this.currentScreen].style.width = '';
this.cursor.style[styles[i]] = '';
this.space.style[styles[i]] = '';
if (styles[i] == 'filter') {
this.console[this.currentScreen].style.width = '';
}
break;
} }
} }
this.scale = 1.0; this.scale = 1.0;
@ -270,10 +264,13 @@ VT100.prototype.reset = function(clearHistory) {
}; };
VT100.prototype.addListener = function(elem, event, listener) { VT100.prototype.addListener = function(elem, event, listener) {
if (elem.addEventListener) { try {
elem.addEventListener(event, listener, false); if (elem.addEventListener) {
} else { elem.addEventListener(event, listener, false);
elem.attachEvent('on' + event, listener); } else {
elem.attachEvent('on' + event, listener);
}
} catch (e) {
} }
}; };
@ -281,11 +278,12 @@ VT100.prototype.getUserSettings = function() {
// Compute hash signature to identify the entries in the userCSS menu. // Compute hash signature to identify the entries in the userCSS menu.
// If the menu is unchanged from last time, default values can be // If the menu is unchanged from last time, default values can be
// looked up in a cookie associated with this page. // looked up in a cookie associated with this page.
this.signature = 2; this.signature = 3;
this.utfPreferred = true; this.utfPreferred = true;
this.visualBell = typeof suppressAllAudio != 'undefined' && this.visualBell = typeof suppressAllAudio != 'undefined' &&
suppressAllAudio; suppressAllAudio;
this.autoprint = true; this.autoprint = true;
this.softKeyboard = false;
this.blinkingCursor = true; this.blinkingCursor = true;
if (this.visualBell) { if (this.visualBell) {
this.signature = Math.floor(16807*this.signature + 1) % this.signature = Math.floor(16807*this.signature + 1) %
@ -311,15 +309,16 @@ VT100.prototype.getUserSettings = function() {
if (settings >= 0) { if (settings >= 0) {
settings = document.cookie.substr(settings + key.length). settings = document.cookie.substr(settings + key.length).
replace(/([0-1]*).*/, "$1"); replace(/([0-1]*).*/, "$1");
if (settings.length == 3 + (typeof userCSSList == 'undefined' ? if (settings.length == 5 + (typeof userCSSList == 'undefined' ?
0 : userCSSList.length)) { 0 : userCSSList.length)) {
this.utfPreferred = settings.charAt(0) != '0'; this.utfPreferred = settings.charAt(0) != '0';
this.visualBell = settings.charAt(1) != '0'; this.visualBell = settings.charAt(1) != '0';
this.autoprint = settings.charAt(2) != '0'; this.autoprint = settings.charAt(2) != '0';
this.blinkingCursor = settings.charAt(3) != '0'; this.softKeyboard = settings.charAt(3) != '0';
this.blinkingCursor = settings.charAt(4) != '0';
if (typeof userCSSList != 'undefined') { if (typeof userCSSList != 'undefined') {
for (var i = 0; i < userCSSList.length; ++i) { for (var i = 0; i < userCSSList.length; ++i) {
userCSSList[i][2] = settings.charAt(i + 3) != '0'; userCSSList[i][2] = settings.charAt(i + 5) != '0';
} }
} }
} }
@ -332,6 +331,7 @@ VT100.prototype.storeUserSettings = function() {
(this.utfEnabled ? '1' : '0') + (this.utfEnabled ? '1' : '0') +
(this.visualBell ? '1' : '0') + (this.visualBell ? '1' : '0') +
(this.autoprint ? '1' : '0') + (this.autoprint ? '1' : '0') +
(this.softKeyboard ? '1' : '0') +
(this.blinkingCursor ? '1' : '0'); (this.blinkingCursor ? '1' : '0');
if (typeof userCSSList != 'undefined') { if (typeof userCSSList != 'undefined') {
for (var i = 0; i < userCSSList.length; ++i) { for (var i = 0; i < userCSSList.length; ++i) {
@ -413,7 +413,7 @@ VT100.prototype.initializeUserCSSStyles = function() {
label.textContent= label.textContent; label.textContent= label.textContent;
} }
// User style sheets are number sequentially // User style sheets are numbered sequentially
var sheet = document.getElementById( var sheet = document.getElementById(
'usercss-' + i); 'usercss-' + i);
if (i == current) { if (i == current) {
@ -470,6 +470,328 @@ VT100.prototype.initializeUserCSSStyles = function() {
} }
}; };
VT100.prototype.resetLastSelectedKey = function(e) {
var key = this.lastSelectedKey;
if (!key) {
return false;
}
var position = this.mousePosition(e);
// We don't get all the necessary events to reliably reselect a key
// if we moved away from it and then back onto it. We approximate the
// behavior by remembering the key until either we release the mouse
// button (we might never get this event if the mouse has since left
// the window), or until we move away too far.
var box = this.keyboard.firstChild;
if (position[0] < box.offsetLeft + key.offsetWidth ||
position[1] < box.offsetTop + key.offsetHeight ||
position[0] >= box.offsetLeft + box.offsetWidth - key.offsetWidth ||
position[1] >= box.offsetTop + box.offsetHeight - key.offsetHeight ||
position[0] < box.offsetLeft + key.offsetLeft - key.offsetWidth ||
position[1] < box.offsetTop + key.offsetTop - key.offsetHeight ||
position[0] >= box.offsetLeft + key.offsetLeft + 2*key.offsetWidth ||
position[1] >= box.offsetTop + key.offsetTop + 2*key.offsetHeight) {
if (this.lastSelectedKey.className) log.console('reset: deselecting');
this.lastSelectedKey.className = '';
this.lastSelectedKey = undefined;
}
return false;
};
VT100.prototype.showShiftState = function(state) {
var style = document.getElementById('shift_state');
if (state) {
this.setTextContentRaw(style,
'#vt100 #keyboard .shifted {' +
'display: inline }' +
'#vt100 #keyboard .unshifted {' +
'display: none }');
} else {
this.setTextContentRaw(style, '');
}
var elems = this.keyboard.getElementsByTagName('I');
for (var i = 0; i < elems.length; ++i) {
if (elems[i].id == '16') {
elems[i].className = state ? 'selected' : '';
}
}
};
VT100.prototype.showCtrlState = function(state) {
var ctrl = this.getChildById(this.keyboard, '17' /* Ctrl */);
if (ctrl) {
ctrl.className = state ? 'selected' : '';
}
};
VT100.prototype.showAltState = function(state) {
var alt = this.getChildById(this.keyboard, '18' /* Alt */);
if (alt) {
alt.className = state ? 'selected' : '';
}
};
VT100.prototype.clickedKeyboard = function(e, elem, ch, key, shift, ctrl, alt){
var fake = [ ];
fake.charCode = ch;
fake.keyCode = key;
fake.ctrlKey = ctrl;
fake.shiftKey = shift;
fake.altKey = alt;
fake.metaKey = alt;
return this.handleKey(fake);
};
VT100.prototype.addKeyBinding = function(elem, ch, key, CH, KEY) {
if (elem == undefined) {
return;
}
if (ch == '\u00A0') {
// &nbsp; should be treated as a regular space character.
ch = ' ';
}
if (ch != undefined && CH == undefined) {
// For letter keys, we automatically compute the uppercase character code
// from the lowercase one.
CH = ch.toUpperCase();
}
if (KEY == undefined && key != undefined) {
// Most keys have identically key codes for both lowercase and uppercase
// keypresses. Normally, only function keys would have distinct key codes,
// whereas regular keys have character codes.
KEY = key;
} else if (KEY == undefined && CH != undefined) {
// For regular keys, copy the character code to the key code.
KEY = CH.charCodeAt(0);
}
if (key == undefined && ch != undefined) {
// For regular keys, copy the character code to the key code.
key = ch.charCodeAt(0);
}
// Convert characters to numeric character codes. If the character code
// is undefined (i.e. this is a function key), set it to zero.
ch = ch ? ch.charCodeAt(0) : 0;
CH = CH ? CH.charCodeAt(0) : 0;
// Mouse down events high light the key. We also set lastSelectedKey. This
// is needed to that mouseout/mouseover can keep track of the key that
// is currently being clicked.
this.addListener(elem, 'mousedown',
function(vt100, elem, key) { return function(e) {
if ((e.which || e.button) == 1) {
if (vt100.lastSelectedKey) {
vt100.lastSelectedKey.className= '';
}
// Highlight the key while the mouse button is held down.
if (key == 16 /* Shift */) {
if (!elem.className != vt100.isShift) {
vt100.showShiftState(!vt100.isShift);
}
} else if (key == 17 /* Ctrl */) {
if (!elem.className != vt100.isCtrl) {
vt100.showCtrlState(!vt100.isCtrl);
}
} else if (key == 18 /* Alt */) {
if (!elem.className != vt100.isAlt) {
vt100.showAltState(!vt100.isAlt);
}
} else {
elem.className = 'selected';
}
vt100.lastSelectedKey = elem;
}
return false; }; }(this, elem, key));
var clicked =
// Modifier keys update the state of the keyboard, but do not generate
// any key clicks that get forwarded to the application.
key >= 16 /* Shift */ && key <= 18 /* Alt */ ?
function(vt100, elem) { return function(e) {
if (elem == vt100.lastSelectedKey) {
if (key == 16 /* Shift */) {
// The user clicked the Shift key
vt100.isShift = !vt100.isShift;
vt100.showShiftState(vt100.isShift);
} else if (key == 17 /* Ctrl */) {
vt100.isCtrl = !vt100.isCtrl;
vt100.showCtrlState(vt100.isCtrl);
} else if (key == 18 /* Alt */) {
vt100.isAlt = !vt100.isAlt;
vt100.showAltState(vt100.isAlt);
}
vt100.lastSelectedKey = undefined;
}
if (vt100.lastSelectedKey) {
vt100.lastSelectedKey.className = '';
vt100.lastSelectedKey = undefined;
}
return false; }; }(this, elem) :
// Regular keys generate key clicks, when the mouse button is released or
// when a mouse click event is received.
function(vt100, elem, ch, key, CH, KEY) { return function(e) {
if (vt100.lastSelectedKey) {
if (elem == vt100.lastSelectedKey) {
// The user clicked a key.
if (vt100.isShift) {
vt100.clickedKeyboard(e, elem, CH, KEY,
true, vt100.isCtrl, vt100.isAlt);
} else {
vt100.clickedKeyboard(e, elem, ch, key,
false, vt100.isCtrl, vt100.isAlt);
}
vt100.isShift = false;
vt100.showShiftState(false);
vt100.isCtrl = false;
vt100.showCtrlState(false);
vt100.isAlt = false;
vt100.showAltState(false);
}
vt100.lastSelectedKey.className = '';
vt100.lastSelectedKey = undefined;
}
elem.className = '';
return false; }; }(this, elem, ch, key, CH, KEY);
this.addListener(elem, 'mouseup', clicked);
this.addListener(elem, 'click', clicked);
// When moving the mouse away from a key, check if any keys need to be
// deselected.
this.addListener(elem, 'mouseout',
function(vt100, elem, key) { return function(e) {
if (key == 16 /* Shift */) {
if (!elem.className == vt100.isShift) {
vt100.showShiftState(vt100.isShift);
}
} else if (key == 17 /* Ctrl */) {
if (!elem.className == vt100.isCtrl) {
vt100.showCtrlState(vt100.isCtrl);
}
} else if (key == 18 /* Alt */) {
if (!elem.className == vt100.isAlt) {
vt100.showAltState(vt100.isAlt);
}
} else if (elem.className) {
elem.className = '';
vt100.lastSelectedKey = elem;
} else if (vt100.lastSelectedKey) {
vt100.resetLastSelectedKey(e);
}
return false; }; }(this, elem, key));
// When moving the mouse over a key, select it if the user is still holding
// the mouse button down (i.e. elem == lastSelectedKey)
this.addListener(elem, 'mouseover',
function(vt100, elem, key) { return function(e) {
if (elem == vt100.lastSelectedKey) {
if (key == 16 /* Shift */) {
if (!elem.className != vt100.isShift) {
vt100.showShiftState(!vt100.isShift);
}
} else if (key == 17 /* Ctrl */) {
if (!elem.className != vt100.isCtrl) {
vt100.showCtrlState(!vt100.isCtrl);
}
} else if (key == 18 /* Alt */) {
if (!elem.className != vt100.isAlt) {
vt100.showAltState(!vt100.isAlt);
}
} else if (!elem.className) {
elem.className = 'selected';
}
} else {
vt100.resetLastSelectedKey(e);
}
return false; }; }(this, elem, key));
};
VT100.prototype.initializeKeyBindings = function(elem) {
if (elem) {
if (elem.nodeName == "I" || elem.nodeName == "B") {
if (elem.id) {
// Function keys. The Javascript keycode is part of the "id"
var i = parseInt(elem.id);
if (i) {
// If the id does not parse as a number, it is not a keycode.
this.addKeyBinding(elem, undefined, i);
}
} else {
var child = elem.firstChild;
if (child.nodeName == "#text") {
// If the key only has a text node as a child, then it is a letter.
// Automatically compute the lower and upper case version of the key.
this.addKeyBinding(elem, this.getTextContent(child).toLowerCase());
} else {
// If the key has two children, they are the lower and upper case
// character code, respectively.
this.addKeyBinding(elem, this.getTextContent(child), undefined,
this.getTextContent(child.nextSibling));
}
}
}
}
// Recursively parse all other child nodes.
for (elem = elem.firstChild; elem; elem = elem.nextSibling) {
this.initializeKeyBindings(elem);
}
};
VT100.prototype.initializeKeyboard = function() {
// Configure mouse event handlers for button that displays/hides keyboard
var box = this.keyboard.firstChild;
this.hideSoftKeyboard();
this.addListener(this.keyboardImage, 'click',
function(vt100) { return function(e) {
if (vt100.keyboard.style.display != '') {
if (vt100.reconnectBtn.style.visibility != '') {
vt100.showSoftKeyboard();
}
} else {
vt100.hideSoftKeyboard();
vt100.input.focus();
}
return false; }; }(this));
// Enable button that displays keyboard
if (this.softKeyboard) {
this.keyboardImage.style.visibility = 'visible';
}
// Configure mouse event handlers for on-screen keyboard
this.addListener(this.keyboard, 'click',
function(vt100) { return function(e) {
vt100.hideSoftKeyboard();
vt100.input.focus();
return false; }; }(this));
this.addListener(this.keyboard, 'selectstart', this.cancelEvent);
this.addListener(box, 'click', this.cancelEvent);
this.addListener(box, 'mouseup',
function(vt100) { return function(e) {
if (vt100.lastSelectedKey) {
vt100.lastSelectedKey.className = '';
vt100.lastSelectedKey = undefined;
}
return false; }; }(this));
this.addListener(box, 'mouseout',
function(vt100) { return function(e) {
return vt100.resetLastSelectedKey(e); }; }(this));
this.addListener(box, 'mouseover',
function(vt100) { return function(e) {
return vt100.resetLastSelectedKey(e); }; }(this));
// Configure SHIFT key behavior
var style = document.createElement('style');
var id = document.createAttribute('id');
id.nodeValue = 'shift_state';
style.setAttributeNode(id);
var type = document.createAttribute('type');
type.nodeValue = 'text/css';
style.setAttributeNode(type);
document.getElementsByTagName('head')[0].appendChild(style);
// Set up key bindings
this.initializeKeyBindings(box);
};
VT100.prototype.initializeElements = function(container) { VT100.prototype.initializeElements = function(container) {
// If the necessary objects have not already been defined in the HTML // If the necessary objects have not already been defined in the HTML
// page, create them now. // page, create them now.
@ -483,6 +805,9 @@ VT100.prototype.initializeElements = function(container) {
if (!this.getChildById(this.container, 'reconnect') || if (!this.getChildById(this.container, 'reconnect') ||
!this.getChildById(this.container, 'menu') || !this.getChildById(this.container, 'menu') ||
!this.getChildById(this.container, 'keyboard') ||
!this.getChildById(this.container, 'kbd_button') ||
!this.getChildById(this.container, 'kbd_img') ||
!this.getChildById(this.container, 'scrollable') || !this.getChildById(this.container, 'scrollable') ||
!this.getChildById(this.container, 'console') || !this.getChildById(this.container, 'console') ||
!this.getChildById(this.container, 'alt_console') || !this.getChildById(this.container, 'alt_console') ||
@ -525,7 +850,15 @@ VT100.prototype.initializeElements = function(container) {
'<div id="cursize" style="visibility: hidden">' + '<div id="cursize" style="visibility: hidden">' +
'</div>' + '</div>' +
'<div id="menu"></div>' + '<div id="menu"></div>' +
'<div id="keyboard" unselectable="on">' +
KEYBOARD +
'</div>' +
'<div id="scrollable">' + '<div id="scrollable">' +
'<table id="kbd_button">' +
'<tr><td width="100%">&nbsp;</td>' +
'<td><img id="kbd_img" src="keyboard.png" /></td>' +
'<td>&nbsp;&nbsp;&nbsp;&nbsp;</td></tr>' +
'</table>' +
'<pre id="lineheight">&nbsp;</pre>' + '<pre id="lineheight">&nbsp;</pre>' +
'<pre id="console">' + '<pre id="console">' +
'<pre></pre>' + '<pre></pre>' +
@ -566,6 +899,8 @@ VT100.prototype.initializeElements = function(container) {
this.reconnectBtn = this.getChildById(this.container,'reconnect'); this.reconnectBtn = this.getChildById(this.container,'reconnect');
this.curSizeBox = this.getChildById(this.container, 'cursize'); this.curSizeBox = this.getChildById(this.container, 'cursize');
this.menu = this.getChildById(this.container, 'menu'); this.menu = this.getChildById(this.container, 'menu');
this.keyboard = this.getChildById(this.container, 'keyboard');
this.keyboardImage = this.getChildById(this.container, 'kbd_img');
this.scrollable = this.getChildById(this.container, this.scrollable = this.getChildById(this.container,
'scrollable'); 'scrollable');
this.lineheight = this.getChildById(this.container, this.lineheight = this.getChildById(this.container,
@ -646,6 +981,9 @@ VT100.prototype.initializeElements = function(container) {
// Hide context menu // Hide context menu
this.hideContextMenu(); this.hideContextMenu();
// Set up onscreen soft keyboard
this.initializeKeyboard();
// Add listener to reconnect button // Add listener to reconnect button
this.addListener(this.reconnectBtn.firstChild, 'click', this.addListener(this.reconnectBtn.firstChild, 'click',
function(vt100) { function(vt100) {
@ -733,6 +1071,7 @@ VT100.prototype.reconnect = function() {
VT100.prototype.showReconnect = function(state) { VT100.prototype.showReconnect = function(state) {
if (state) { if (state) {
this.hideSoftKeyboard();
this.reconnectBtn.style.visibility = ''; this.reconnectBtn.style.visibility = '';
} else { } else {
this.reconnectBtn.style.visibility = 'hidden'; this.reconnectBtn.style.visibility = 'hidden';
@ -766,6 +1105,9 @@ VT100.prototype.resized = function(w, h) {
}; };
VT100.prototype.resizer = function() { VT100.prototype.resizer = function() {
// Hide onscreen soft keyboard
this.hideSoftKeyboard();
// The cursor can get corrupted if the print-preview is displayed in Firefox. // The cursor can get corrupted if the print-preview is displayed in Firefox.
// Recreating it, will repair it. // Recreating it, will repair it.
var newCursor = document.createElement('pre'); var newCursor = document.createElement('pre');
@ -945,6 +1287,17 @@ VT100.prototype.cancelEvent = function(event) {
return false; return false;
}; };
VT100.prototype.mousePosition = function(event) {
var offsetX = this.container.offsetLeft;
var offsetY = this.container.offsetTop;
for (var e = this.container; e = e.offsetParent; ) {
offsetX += e.offsetLeft;
offsetY += e.offsetTop;
}
return [ event.clientX - offsetX,
event.clientY - offsetY ];
};
VT100.prototype.mouseEvent = function(event, type) { VT100.prototype.mouseEvent = function(event, type) {
// If any text is currently selected, do not move the focus as that would // If any text is currently selected, do not move the focus as that would
// invalidate the selection. // invalidate the selection.
@ -954,15 +1307,10 @@ VT100.prototype.mouseEvent = function(event, type) {
} }
// Compute mouse position in characters. // Compute mouse position in characters.
var offsetX = this.container.offsetLeft; var position = this.mousePosition(event);
var offsetY = this.container.offsetTop; var x = Math.floor(position[0] / this.cursorWidth);
for (var e = this.container; e = e.offsetParent; ) { var y = Math.floor((position[1] + this.scrollable.scrollTop) /
offsetX += e.offsetLeft; this.cursorHeight) - this.numScrollbackLines;
offsetY += e.offsetTop;
}
var x = (event.clientX - offsetX) / this.cursorWidth;
var y = ((event.clientY - offsetY) + this.scrollable.offsetTop) /
this.cursorHeight - this.numScrollbackLines;
var inside = true; var inside = true;
if (x >= this.terminalWidth) { if (x >= this.terminalWidth) {
x = this.terminalWidth - 1; x = this.terminalWidth - 1;
@ -1022,7 +1370,7 @@ VT100.prototype.mouseEvent = function(event, type) {
// Bring up context menu. // Bring up context menu.
if (button == 2 && !event.shiftKey) { if (button == 2 && !event.shiftKey) {
if (type == MOUSE_DOWN) { if (type == MOUSE_DOWN) {
this.showContextMenu(event.clientX - offsetX, event.clientY - offsetY); this.showContextMenu(position[0], position[1]);
} }
return this.cancelEvent(event); return this.cancelEvent(event);
} }
@ -1058,6 +1406,29 @@ VT100.prototype.getTextContent = function(elem) {
(typeof elem.textContent == 'undefined' ? elem.innerText : ''); (typeof elem.textContent == 'undefined' ? elem.innerText : '');
}; };
VT100.prototype.setTextContentRaw = function(elem, s) {
// Updating the content of an element is an expensive operation. It actually
// pays off to first check whether the element is still unchanged.
if (typeof elem.textContent == 'undefined') {
if (elem.innerText != s) {
try {
elem.innerText = s;
} catch (e) {
// Very old versions of IE do not allow setting innerText. Instead,
// remove all children, by setting innerHTML and then set the text
// using DOM methods.
elem.innerHTML = '';
elem.appendChild(document.createTextNode(
this.replaceChar(s, ' ', '\u00A0')));
}
}
} else {
if (elem.textContent != s) {
elem.textContent = s;
}
}
};
VT100.prototype.setTextContent = function(elem, s) { VT100.prototype.setTextContent = function(elem, s) {
// Check if we find any URLs in the text. If so, automatically convert them // Check if we find any URLs in the text. If so, automatically convert them
// to links. // to links.
@ -1103,26 +1474,7 @@ VT100.prototype.setTextContent = function(elem, s) {
return; return;
} }
// Updating the content of an element is an expensive operation. It actually this.setTextContentRaw(elem, s);
// pays off to first check whether the element is still unchanged.
if (typeof elem.textContent == 'undefined') {
if (elem.innerText != s) {
try {
elem.innerText = s;
} catch (e) {
// Very old versions of IE do not allow setting innerText. Instead,
// remove all children, by setting innerHTML and then set the text
// using DOM methods.
elem.innerHTML = '';
elem.appendChild(document.createTextNode(
this.replaceChar(s, ' ', '\u00A0')));
}
}
} else {
if (elem.textContent != s) {
elem.textContent = s;
}
}
}; };
VT100.prototype.insertBlankLine = function(y, color, style) { VT100.prototype.insertBlankLine = function(y, color, style) {
@ -1578,27 +1930,21 @@ VT100.prototype.enableAlternateScreen = function(state) {
this.console[this.currentScreen].style.display = ''; this.console[this.currentScreen].style.display = '';
// Select appropriate character pitch. // Select appropriate character pitch.
var styles = [ 'transform', var transform = this.getTransformName();
'WebkitTransform', if (transform) {
'MozTransform', if (state) {
'filter' ]; // Upon enabling the alternate screen, we switch to 80 column mode. But
for (var i = 0; i < styles.length; ++i) { // upon returning to the regular screen, we restore the mode that was
if (typeof this.console[0].style[styles[i]] != 'undefined') { // in effect previously.
if (state) { this.console[1].style[transform] = '';
// Upon enabling the alternate screen, we switch to 80 column mode. But }
// upon returning to the regular screen, we restore the mode that was var style =
// in effect previously. this.console[this.currentScreen].style[transform];
this.console[1].style[styles[i]] = ''; this.cursor.style[transform] = style;
} this.space.style[transform] = style;
var style = this.scale = style == '' ? 1.0:1.65;
this.console[this.currentScreen].style[styles[i]]; if (transform == 'filter') {
this.cursor.style[styles[i]] = style; this.console[this.currentScreen].style.width = style == '' ? '165%':'';
this.space.style[styles[i]] = style;
this.scale = style == '' ? 1.0:1.65;
if (styles[i] == 'filter') {
this.console[this.currentScreen].style.width = style == '' ? '165%':'';
}
break;
} }
} }
this.resizer(); this.resizer();
@ -1969,6 +2315,70 @@ VT100.prototype.toggleBell = function() {
this.visualBell = !this.visualBell; this.visualBell = !this.visualBell;
}; };
VT100.prototype.toggleSoftKeyboard = function() {
this.softKeyboard = !this.softKeyboard;
this.keyboardImage.style.visibility = this.softKeyboard ? 'visible' : '';
};
VT100.prototype.deselectKeys = function(elem) {
if (elem && elem.className == 'selected') {
elem.className = '';
}
for (elem = elem.firstChild; elem; elem = elem.nextSibling) {
this.deselectKeys(elem);
}
};
VT100.prototype.showSoftKeyboard = function() {
// Make sure no key is currently selected
this.lastSelectedKey = undefined;
this.deselectKeys(this.keyboard);
this.isShift = false;
this.showShiftState(false);
this.isCtrl = false;
this.showCtrlState(false);
this.isAlt = false;
this.showAltState(false);
this.keyboard.style.left = '0px';
this.keyboard.style.top = '0px';
this.keyboard.style.width = this.container.offsetWidth + 'px';
this.keyboard.style.height = this.container.offsetHeight + 'px';
this.keyboard.style.visibility = 'hidden';
this.keyboard.style.display = '';
var kbd = this.keyboard.firstChild;
var scale = 1.0;
var transform = this.getTransformName();
if (transform) {
kbd.style[transform] = '';
if (kbd.offsetWidth > 0.9 * this.container.offsetWidth) {
scale = (kbd.offsetWidth/
this.container.offsetWidth)/0.9;
}
if (kbd.offsetHeight > 0.9 * this.container.offsetHeight) {
scale = Math.max((kbd.offsetHeight/
this.container.offsetHeight)/0.9);
}
var style = this.getTransformStyle(transform,
scale > 1.0 ? scale : undefined);
kbd.style[transform] = style;
}
if (transform == 'filter') {
scale = 1.0;
}
kbd.style.left = ((this.container.offsetWidth -
kbd.offsetWidth/scale)/2) + 'px';
kbd.style.top = ((this.container.offsetHeight -
kbd.offsetHeight/scale)/2) + 'px';
this.keyboard.style.visibility = 'visible';
};
VT100.prototype.hideSoftKeyboard = function() {
this.keyboard.style.display = 'none';
};
VT100.prototype.toggleCursorBlinking = function() { VT100.prototype.toggleCursorBlinking = function() {
this.blinkingCursor = !this.blinkingCursor; this.blinkingCursor = !this.blinkingCursor;
}; };
@ -2007,6 +2417,9 @@ VT100.prototype.showContextMenu = function(x, y) {
'<li>' + '<li>' +
(this.visualBell ? '<img src="enabled.gif" />' : '') + (this.visualBell ? '<img src="enabled.gif" />' : '') +
'Visual Bell</li>'+ 'Visual Bell</li>'+
'<li>' +
(this.softKeyboard ? '<img src="enabled.gif" />' : '') +
'Onscreen Keyboard</li>' +
'<li id="endconfig">' + '<li id="endconfig">' +
(this.blinkingCursor ? '<img src="enabled.gif" />' : '') + (this.blinkingCursor ? '<img src="enabled.gif" />' : '') +
'Blinking Cursor</li>'+ 'Blinking Cursor</li>'+
@ -2038,6 +2451,7 @@ VT100.prototype.showContextMenu = function(x, y) {
// Actions for default items // Actions for default items
var actions = [ this.copyLast, p, this.reset, var actions = [ this.copyLast, p, this.reset,
this.toggleUTF, this.toggleBell, this.toggleUTF, this.toggleBell,
this.toggleSoftKeyboard,
this.toggleCursorBlinking ]; this.toggleCursorBlinking ];
// Actions for user CSS styles (if any) // Actions for user CSS styles (if any)
@ -2093,26 +2507,30 @@ VT100.prototype.showContextMenu = function(x, y) {
} }
// Position menu next to the mouse pointer // Position menu next to the mouse pointer
if (x + popup.clientWidth > this.container.offsetWidth) { this.menu.style.left = '0px';
x = this.container.offsetWidth - popup.clientWidth; this.menu.style.top = '0px';
this.menu.style.width = this.container.offsetWidth + 'px';
this.menu.style.height = this.container.offsetHeight + 'px';
popup.style.left = '0px';
popup.style.top = '0px';
var margin = 2;
if (x + popup.clientWidth >= this.container.offsetWidth - margin) {
x = this.container.offsetWidth-popup.clientWidth - margin - 1;
} }
if (x < 0) { if (x < margin) {
x = 0; x = margin;
} }
if (y + popup.clientHeight > this.container.offsetHeight) { if (y + popup.clientHeight >= this.container.offsetHeight - margin) {
y = this.container.offsetHeight-popup.clientHeight; y = this.container.offsetHeight-popup.clientHeight - margin - 1;
} }
if (y < 0) { if (y < margin) {
y = 0; y = margin;
} }
popup.style.left = x + 'px'; popup.style.left = x + 'px';
popup.style.top = y + 'px'; popup.style.top = y + 'px';
// Block all other interactions with the terminal emulator // Block all other interactions with the terminal emulator
this.menu.style.left = '0px';
this.menu.style.top = '0px';
this.menu.style.width = this.container.offsetWidth + 'px';
this.menu.style.height = this.container.offsetHeight + 'px';
this.addListener(this.menu, 'click', function(vt100) { this.addListener(this.menu, 'click', function(vt100) {
return function() { return function() {
vt100.hideContextMenu(); vt100.hideContextMenu();
@ -2895,39 +3313,42 @@ VT100.prototype.restoreCursor = function() {
this.savedY[this.currentScreen]); this.savedY[this.currentScreen]);
}; };
VT100.prototype.set80_132Mode = function(state) { VT100.prototype.getTransformName = function() {
var transform = undefined; var styles = [ 'transform', 'WebkitTransform', 'MozTransform', 'filter' ];
var styles = [ 'transform',
'WebkitTransform',
'MozTransform',
'filter'
];
for (var i = 0; i < styles.length; ++i) { for (var i = 0; i < styles.length; ++i) {
if (typeof this.console[0].style[styles[i]] != 'undefined') { if (typeof this.console[0].style[styles[i]] != 'undefined') {
transform = styles[i]; return styles[i];
break;
} }
} }
return undefined;
};
VT100.prototype.getTransformStyle = function(transform, scale) {
return scale && scale != 1.0
? transform == 'filter'
? 'progid:DXImageTransform.Microsoft.Matrix(' +
'M11=' + (1.0/scale) + ',M12=0,M21=0,M22=1,' +
"sizingMethod='auto expand')"
: 'translateX(-50%) ' +
'scaleX(' + (1.0/scale) + ') ' +
'translateX(50%)'
: '';
};
VT100.prototype.set80_132Mode = function(state) {
var transform = this.getTransformName();
if (transform) { if (transform) {
if ((this.console[this.currentScreen].style[transform] != '') == state) { if ((this.console[this.currentScreen].style[transform] != '') == state) {
return; return;
} }
var style = var style = state ?
state ? transform == 'filter' this.getTransformStyle(transform, 1.65):'';
? 'progid:DXImageTransform.Microsoft.Matrix(' +
'M11=0.606060606060606060606,M12=0,M21=0,M22=1,' +
"sizingMethod='auto expand')"
: 'translateX(-50%) ' +
'scaleX(0.606060606060606060606) ' +
'translateX(50%)'
: '';
this.console[this.currentScreen].style[transform] = style; this.console[this.currentScreen].style[transform] = style;
this.cursor.style[transform] = style; this.cursor.style[transform] = style;
this.space.style[transform] = style; this.space.style[transform] = style;
this.scale = state ? 1.65 : 1.0; this.scale = state ? 1.65 : 1.0;
if (transform == 'filter') { if (transform == 'filter') {
this.console[this.currentScreen].style.width = state ? '165%' : ''; this.console[this.currentScreen].style.width = state ? '165%' : '';
} }
this.resizer(); this.resizer();
} }