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>
* 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.xml \
demo/enabled.gif \
demo/keyboard.png \
demo/styles.css \
demo/print-styles.css \
demo/vt100.js \
@ -106,6 +107,8 @@ shellinaboxd_SOURCES = shellinabox/shellinaboxd.c \
shellinabox/print-styles.css \
shellinabox/enabled.gif \
shellinabox/favicon.ico \
shellinabox/keyboard.png \
shellinabox/keyboard-layout.html \
shellinabox/beep.wav \
config.h
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.jspp \
${top_srcdir}/demo/enabled.gif \
${top_srcdir}/demo/favicon.ico \
${top_srcdir}/demo/keyboard.png \
${top_srcdir}/demo/styles.css \
${top_srcdir}/demo/print-styles.css \
${top_srcdir}/demo/vt100.js \
@ -169,6 +174,10 @@ ${top_srcdir}/demo/favicon.ico: ${top_srcdir}/shellinabox/favicon.ico
@rm -f "$@"
ln "$?" "$@"
${top_srcdir}/demo/keyboard.png: ${top_srcdir}/shellinabox/keyboard.png
@rm -f "$@"
ln "$?" "$@"
${top_srcdir}/demo/styles.css: ${top_srcdir}/shellinabox/styles.css
@rm -f "$@"
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 "$@"
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"; \
echo preprocess "$$src" '>'"$@"; \
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 "$@"; \
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:
@echo $(OBJCOPY) "$<" "$@"
@ -272,15 +289,23 @@ clean-local:
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:
@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 d "$<"`" \
-e "s/^#/\/\/ #/" \
-e "s/VERSION/\"@VERSION@ (revision @VCS_REVISION@)\"/g" \
-e "s%KEYBOARD%'$${kbd}'%" \
"$<" >"$@"
.js.o:

View file

@ -82,6 +82,8 @@ am_shellinaboxd_OBJECTS = shellinaboxd.$(OBJEXT) \
shellinabox/styles.$(OBJEXT) \
shellinabox/print-styles.$(OBJEXT) \
shellinabox/enabled.$(OBJEXT) shellinabox/favicon.$(OBJEXT) \
shellinabox/keyboard.$(OBJEXT) \
shellinabox/keyboard-layout.$(OBJEXT) \
shellinabox/beep.$(OBJEXT)
shellinaboxd_OBJECTS = $(am_shellinaboxd_OBJECTS)
shellinaboxd_DEPENDENCIES = liblogging.la libhttp.la
@ -288,6 +290,7 @@ EXTRA_DIST = INSTALL.Debian \
demo/demo.jspp \
demo/demo.xml \
demo/enabled.gif \
demo/keyboard.png \
demo/styles.css \
demo/print-styles.css \
demo/vt100.js \
@ -366,6 +369,8 @@ shellinaboxd_SOURCES = shellinabox/shellinaboxd.c \
shellinabox/print-styles.css \
shellinabox/enabled.gif \
shellinabox/favicon.ico \
shellinabox/keyboard.png \
shellinabox/keyboard-layout.html \
shellinabox/beep.wav \
config.h
@ -407,7 +412,7 @@ all: config.h
$(MAKE) $(AM_MAKEFLAGS) all-am
.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:
@:
$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
@ -537,6 +542,10 @@ shellinabox/enabled.$(OBJEXT): shellinabox/$(am__dirstamp) \
shellinabox/$(DEPDIR)/$(am__dirstamp)
shellinabox/favicon.$(OBJEXT): shellinabox/$(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/$(DEPDIR)/$(am__dirstamp)
shellinaboxd$(EXEEXT): $(shellinaboxd_OBJECTS) $(shellinaboxd_DEPENDENCIES)
@ -549,6 +558,8 @@ mostlyclean-compile:
-rm -f shellinabox/cgi_root.$(OBJEXT)
-rm -f shellinabox/enabled.$(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/root_page.$(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.jspp \
${top_srcdir}/demo/enabled.gif \
${top_srcdir}/demo/favicon.ico \
${top_srcdir}/demo/keyboard.png \
${top_srcdir}/demo/styles.css \
${top_srcdir}/demo/print-styles.css \
${top_srcdir}/demo/vt100.js \
@ -1183,6 +1196,10 @@ ${top_srcdir}/demo/favicon.ico: ${top_srcdir}/shellinabox/favicon.ico
@rm -f "$@"
ln "$?" "$@"
${top_srcdir}/demo/keyboard.png: ${top_srcdir}/shellinabox/keyboard.png
@rm -f "$@"
ln "$?" "$@"
${top_srcdir}/demo/styles.css: ${top_srcdir}/shellinabox/styles.css
@rm -f "$@"
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 "$@"
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"; \
echo preprocess "$$src" '>'"$@"; \
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 "$@"; \
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:
@echo $(OBJCOPY) "$<" "$@"
@$(OBJCOPY) -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)`\
@ -1283,15 +1309,23 @@ clean-local:
$(OBJCOPY) --add-section .note.GNU-stack=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:
@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 d "$<"`" \
-e "s/^#/\/\/ #/" \
-e "s/VERSION/\"@VERSION@ (revision @VCS_REVISION@)\"/g" \
-e "s%KEYBOARD%'$${kbd}'%" \
"$<" >"$@"
.js.o:

View file

@ -153,7 +153,7 @@
#define STDC_HEADERS 1
/* Most recent revision number in the version control system */
#define VCS_REVISION "220"
#define VCS_REVISION "221"
/* Version number of package */
#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
VCS_REVISION=220
VCS_REVISION=221
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
AC_INIT(shellinabox, 2.10, markus@shellinabox.com)
VCS_REVISION=220
VCS_REVISION=221
AC_SUBST(VCS_REVISION)
AC_DEFINE_UNQUOTED(VCS_REVISION, "${VCS_REVISION}",
[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

@ -136,12 +136,89 @@
#vt100 #scrollable.inverted { color: #ffffff;
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 {
#vt100 .scrollback {
display: none;
}
#vt100 #reconnect, #vt100 #cursor, #vt100 #menu {
#vt100 #reconnect, #vt100 #cursor, #vt100 #menu, #vt100 #kbd_button, #vt100 #keyboard {
visibility: hidden;
}

View file

@ -238,23 +238,17 @@ VT100.prototype.reset = function(clearHistory) {
this.enableAlternateScreen(false);
var wasCompressed = false;
var styles = [ 'transform',
'WebkitTransform',
'MozTransform',
'filter' ];
for (var i = 0; i < styles.length; ++i) {
if (typeof this.console[0].style[styles[i]] != 'undefined') {
for (var j = 0; j < 1; ++j) {
wasCompressed |= this.console[j].style[styles[i]] != '';
this.console[j].style[styles[i]] = '';
var transform = this.getTransformName();
if (transform) {
for (var i = 0; i < 2; ++i) {
wasCompressed |= this.console[i].style[transform] != '';
this.console[i].style[transform] = '';
}
this.cursor.style[styles[i]] = '';
this.space.style[styles[i]] = '';
if (styles[i] == 'filter') {
this.cursor.style[transform] = '';
this.space.style[transform] = '';
if (transform == 'filter') {
this.console[this.currentScreen].style.width = '';
}
break;
}
}
this.scale = 1.0;
if (wasCompressed) {
@ -270,22 +264,26 @@ VT100.prototype.reset = function(clearHistory) {
};
VT100.prototype.addListener = function(elem, event, listener) {
try {
if (elem.addEventListener) {
elem.addEventListener(event, listener, false);
} else {
elem.attachEvent('on' + event, listener);
}
} catch (e) {
}
};
VT100.prototype.getUserSettings = function() {
// Compute hash signature to identify the entries in the userCSS menu.
// If the menu is unchanged from last time, default values can be
// looked up in a cookie associated with this page.
this.signature = 2;
this.signature = 3;
this.utfPreferred = true;
this.visualBell = typeof suppressAllAudio != 'undefined' &&
suppressAllAudio;
this.autoprint = true;
this.softKeyboard = false;
this.blinkingCursor = true;
if (this.visualBell) {
this.signature = Math.floor(16807*this.signature + 1) %
@ -311,15 +309,16 @@ VT100.prototype.getUserSettings = function() {
if (settings >= 0) {
settings = document.cookie.substr(settings + key.length).
replace(/([0-1]*).*/, "$1");
if (settings.length == 3 + (typeof userCSSList == 'undefined' ?
if (settings.length == 5 + (typeof userCSSList == 'undefined' ?
0 : userCSSList.length)) {
this.utfPreferred = settings.charAt(0) != '0';
this.visualBell = settings.charAt(1) != '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') {
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.visualBell ? '1' : '0') +
(this.autoprint ? '1' : '0') +
(this.softKeyboard ? '1' : '0') +
(this.blinkingCursor ? '1' : '0');
if (typeof userCSSList != 'undefined') {
for (var i = 0; i < userCSSList.length; ++i) {
@ -413,7 +413,7 @@ VT100.prototype.initializeUserCSSStyles = function() {
label.textContent= label.textContent;
}
// User style sheets are number sequentially
// User style sheets are numbered sequentially
var sheet = document.getElementById(
'usercss-' + i);
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) {
// If the necessary objects have not already been defined in the HTML
// page, create them now.
@ -483,6 +805,9 @@ VT100.prototype.initializeElements = function(container) {
if (!this.getChildById(this.container, 'reconnect') ||
!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, 'console') ||
!this.getChildById(this.container, 'alt_console') ||
@ -525,7 +850,15 @@ VT100.prototype.initializeElements = function(container) {
'<div id="cursize" style="visibility: hidden">' +
'</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">' +
'<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="console">' +
'<pre></pre>' +
@ -566,6 +899,8 @@ VT100.prototype.initializeElements = function(container) {
this.reconnectBtn = this.getChildById(this.container,'reconnect');
this.curSizeBox = this.getChildById(this.container, 'cursize');
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,
'scrollable');
this.lineheight = this.getChildById(this.container,
@ -646,6 +981,9 @@ VT100.prototype.initializeElements = function(container) {
// Hide context menu
this.hideContextMenu();
// Set up onscreen soft keyboard
this.initializeKeyboard();
// Add listener to reconnect button
this.addListener(this.reconnectBtn.firstChild, 'click',
function(vt100) {
@ -733,6 +1071,7 @@ VT100.prototype.reconnect = function() {
VT100.prototype.showReconnect = function(state) {
if (state) {
this.hideSoftKeyboard();
this.reconnectBtn.style.visibility = '';
} else {
this.reconnectBtn.style.visibility = 'hidden';
@ -766,6 +1105,9 @@ VT100.prototype.resized = function(w, h) {
};
VT100.prototype.resizer = function() {
// Hide onscreen soft keyboard
this.hideSoftKeyboard();
// The cursor can get corrupted if the print-preview is displayed in Firefox.
// Recreating it, will repair it.
var newCursor = document.createElement('pre');
@ -945,6 +1287,17 @@ VT100.prototype.cancelEvent = function(event) {
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) {
// If any text is currently selected, do not move the focus as that would
// invalidate the selection.
@ -954,15 +1307,10 @@ VT100.prototype.mouseEvent = function(event, type) {
}
// Compute mouse position in characters.
var offsetX = this.container.offsetLeft;
var offsetY = this.container.offsetTop;
for (var e = this.container; e = e.offsetParent; ) {
offsetX += e.offsetLeft;
offsetY += e.offsetTop;
}
var x = (event.clientX - offsetX) / this.cursorWidth;
var y = ((event.clientY - offsetY) + this.scrollable.offsetTop) /
this.cursorHeight - this.numScrollbackLines;
var position = this.mousePosition(event);
var x = Math.floor(position[0] / this.cursorWidth);
var y = Math.floor((position[1] + this.scrollable.scrollTop) /
this.cursorHeight) - this.numScrollbackLines;
var inside = true;
if (x >= this.terminalWidth) {
x = this.terminalWidth - 1;
@ -1022,7 +1370,7 @@ VT100.prototype.mouseEvent = function(event, type) {
// Bring up context menu.
if (button == 2 && !event.shiftKey) {
if (type == 0 /* MOUSE_DOWN */) {
this.showContextMenu(event.clientX - offsetX, event.clientY - offsetY);
this.showContextMenu(position[0], position[1]);
}
return this.cancelEvent(event);
}
@ -1058,6 +1406,29 @@ VT100.prototype.getTextContent = function(elem) {
(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) {
// Check if we find any URLs in the text. If so, automatically convert them
// to links.
@ -1103,26 +1474,7 @@ VT100.prototype.setTextContent = function(elem, s) {
return;
}
// 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;
}
}
this.setTextContentRaw(elem, s);
};
VT100.prototype.insertBlankLine = function(y, color, style) {
@ -1578,28 +1930,22 @@ VT100.prototype.enableAlternateScreen = function(state) {
this.console[this.currentScreen].style.display = '';
// Select appropriate character pitch.
var styles = [ 'transform',
'WebkitTransform',
'MozTransform',
'filter' ];
for (var i = 0; i < styles.length; ++i) {
if (typeof this.console[0].style[styles[i]] != 'undefined') {
var transform = this.getTransformName();
if (transform) {
if (state) {
// Upon enabling the alternate screen, we switch to 80 column mode. But
// upon returning to the regular screen, we restore the mode that was
// in effect previously.
this.console[1].style[styles[i]] = '';
this.console[1].style[transform] = '';
}
var style =
this.console[this.currentScreen].style[styles[i]];
this.cursor.style[styles[i]] = style;
this.space.style[styles[i]] = style;
this.console[this.currentScreen].style[transform];
this.cursor.style[transform] = style;
this.space.style[transform] = style;
this.scale = style == '' ? 1.0:1.65;
if (styles[i] == 'filter') {
if (transform == 'filter') {
this.console[this.currentScreen].style.width = style == '' ? '165%':'';
}
break;
}
}
this.resizer();
@ -1969,12 +2315,76 @@ VT100.prototype.toggleBell = function() {
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() {
this.blinkingCursor = !this.blinkingCursor;
};
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" +
"For more information check http://shellinabox.com");
};
@ -2007,6 +2417,9 @@ VT100.prototype.showContextMenu = function(x, y) {
'<li>' +
(this.visualBell ? '<img src="enabled.gif" />' : '') +
'Visual Bell</li>'+
'<li>' +
(this.softKeyboard ? '<img src="enabled.gif" />' : '') +
'Onscreen Keyboard</li>' +
'<li id="endconfig">' +
(this.blinkingCursor ? '<img src="enabled.gif" />' : '') +
'Blinking Cursor</li>'+
@ -2038,6 +2451,7 @@ VT100.prototype.showContextMenu = function(x, y) {
// Actions for default items
var actions = [ this.copyLast, p, this.reset,
this.toggleUTF, this.toggleBell,
this.toggleSoftKeyboard,
this.toggleCursorBlinking ];
// Actions for user CSS styles (if any)
@ -2093,26 +2507,30 @@ VT100.prototype.showContextMenu = function(x, y) {
}
// Position menu next to the mouse pointer
if (x + popup.clientWidth > this.container.offsetWidth) {
x = this.container.offsetWidth - popup.clientWidth;
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';
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) {
x = 0;
if (x < margin) {
x = margin;
}
if (y + popup.clientHeight > this.container.offsetHeight) {
y = this.container.offsetHeight-popup.clientHeight;
if (y + popup.clientHeight >= this.container.offsetHeight - margin) {
y = this.container.offsetHeight-popup.clientHeight - margin - 1;
}
if (y < 0) {
y = 0;
if (y < margin) {
y = margin;
}
popup.style.left = x + 'px';
popup.style.top = y + 'px';
// 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) {
return function() {
vt100.hideContextMenu();
@ -2895,33 +3313,36 @@ VT100.prototype.restoreCursor = function() {
this.savedY[this.currentScreen]);
};
VT100.prototype.set80_132Mode = function(state) {
var transform = undefined;
var styles = [ 'transform',
'WebkitTransform',
'MozTransform',
'filter'
];
VT100.prototype.getTransformName = function() {
var styles = [ 'transform', 'WebkitTransform', 'MozTransform', 'filter' ];
for (var i = 0; i < styles.length; ++i) {
if (typeof this.console[0].style[styles[i]] != 'undefined') {
transform = styles[i];
break;
return styles[i];
}
}
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 ((this.console[this.currentScreen].style[transform] != '') == state) {
return;
}
var style =
state ? transform == 'filter'
? 'progid:DXImageTransform.Microsoft.Matrix(' +
'M11=0.606060606060606060606,M12=0,M21=0,M22=1,' +
"sizingMethod='auto expand')"
: 'translateX(-50%) ' +
'scaleX(0.606060606060606060606) ' +
'translateX(50%)'
: '';
var style = state ?
this.getTransformStyle(transform, 1.65):'';
this.console[this.currentScreen].style[transform] = style;
this.cursor.style[transform] = style;
this.space.style[transform] = style;

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() {
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" +
"For more information check http://shellinabox.com" +
(typeof serverSupportsSSL != 'undefined' && serverSupportsSSL ?

View file

@ -641,6 +641,11 @@ static int shellInABoxHttpHandler(HttpConnection *http, void *arg,
extern char faviconStart[];
extern char 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)) {
// 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.

View file

@ -135,6 +135,83 @@
#vt100 #scrollable.inverted { color: #ffffff;
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]
/* IE cannot properly handle "inherit" properties. So, the monochrome.css/
* color.css style sheets cannot work, if we define colors in styles.css.
@ -180,7 +257,7 @@
display: none;
}
#vt100 #reconnect, #vt100 #cursor, #vt100 #menu {
#vt100 #reconnect, #vt100 #cursor, #vt100 #menu, #vt100 #kbd_button, #vt100 #keyboard {
visibility: hidden;
}

View file

@ -238,23 +238,17 @@ VT100.prototype.reset = function(clearHistory) {
this.enableAlternateScreen(false);
var wasCompressed = false;
var styles = [ 'transform',
'WebkitTransform',
'MozTransform',
'filter' ];
for (var i = 0; i < styles.length; ++i) {
if (typeof this.console[0].style[styles[i]] != 'undefined') {
for (var j = 0; j < 1; ++j) {
wasCompressed |= this.console[j].style[styles[i]] != '';
this.console[j].style[styles[i]] = '';
var transform = this.getTransformName();
if (transform) {
for (var i = 0; i < 2; ++i) {
wasCompressed |= this.console[i].style[transform] != '';
this.console[i].style[transform] = '';
}
this.cursor.style[styles[i]] = '';
this.space.style[styles[i]] = '';
if (styles[i] == 'filter') {
this.cursor.style[transform] = '';
this.space.style[transform] = '';
if (transform == 'filter') {
this.console[this.currentScreen].style.width = '';
}
break;
}
}
this.scale = 1.0;
if (wasCompressed) {
@ -270,22 +264,26 @@ VT100.prototype.reset = function(clearHistory) {
};
VT100.prototype.addListener = function(elem, event, listener) {
try {
if (elem.addEventListener) {
elem.addEventListener(event, listener, false);
} else {
elem.attachEvent('on' + event, listener);
}
} catch (e) {
}
};
VT100.prototype.getUserSettings = function() {
// Compute hash signature to identify the entries in the userCSS menu.
// If the menu is unchanged from last time, default values can be
// looked up in a cookie associated with this page.
this.signature = 2;
this.signature = 3;
this.utfPreferred = true;
this.visualBell = typeof suppressAllAudio != 'undefined' &&
suppressAllAudio;
this.autoprint = true;
this.softKeyboard = false;
this.blinkingCursor = true;
if (this.visualBell) {
this.signature = Math.floor(16807*this.signature + 1) %
@ -311,15 +309,16 @@ VT100.prototype.getUserSettings = function() {
if (settings >= 0) {
settings = document.cookie.substr(settings + key.length).
replace(/([0-1]*).*/, "$1");
if (settings.length == 3 + (typeof userCSSList == 'undefined' ?
if (settings.length == 5 + (typeof userCSSList == 'undefined' ?
0 : userCSSList.length)) {
this.utfPreferred = settings.charAt(0) != '0';
this.visualBell = settings.charAt(1) != '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') {
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.visualBell ? '1' : '0') +
(this.autoprint ? '1' : '0') +
(this.softKeyboard ? '1' : '0') +
(this.blinkingCursor ? '1' : '0');
if (typeof userCSSList != 'undefined') {
for (var i = 0; i < userCSSList.length; ++i) {
@ -413,7 +413,7 @@ VT100.prototype.initializeUserCSSStyles = function() {
label.textContent= label.textContent;
}
// User style sheets are number sequentially
// User style sheets are numbered sequentially
var sheet = document.getElementById(
'usercss-' + i);
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) {
// If the necessary objects have not already been defined in the HTML
// page, create them now.
@ -483,6 +805,9 @@ VT100.prototype.initializeElements = function(container) {
if (!this.getChildById(this.container, 'reconnect') ||
!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, 'console') ||
!this.getChildById(this.container, 'alt_console') ||
@ -525,7 +850,15 @@ VT100.prototype.initializeElements = function(container) {
'<div id="cursize" style="visibility: hidden">' +
'</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">' +
'<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="console">' +
'<pre></pre>' +
@ -566,6 +899,8 @@ VT100.prototype.initializeElements = function(container) {
this.reconnectBtn = this.getChildById(this.container,'reconnect');
this.curSizeBox = this.getChildById(this.container, 'cursize');
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,
'scrollable');
this.lineheight = this.getChildById(this.container,
@ -646,6 +981,9 @@ VT100.prototype.initializeElements = function(container) {
// Hide context menu
this.hideContextMenu();
// Set up onscreen soft keyboard
this.initializeKeyboard();
// Add listener to reconnect button
this.addListener(this.reconnectBtn.firstChild, 'click',
function(vt100) {
@ -733,6 +1071,7 @@ VT100.prototype.reconnect = function() {
VT100.prototype.showReconnect = function(state) {
if (state) {
this.hideSoftKeyboard();
this.reconnectBtn.style.visibility = '';
} else {
this.reconnectBtn.style.visibility = 'hidden';
@ -766,6 +1105,9 @@ VT100.prototype.resized = function(w, h) {
};
VT100.prototype.resizer = function() {
// Hide onscreen soft keyboard
this.hideSoftKeyboard();
// The cursor can get corrupted if the print-preview is displayed in Firefox.
// Recreating it, will repair it.
var newCursor = document.createElement('pre');
@ -945,6 +1287,17 @@ VT100.prototype.cancelEvent = function(event) {
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) {
// If any text is currently selected, do not move the focus as that would
// invalidate the selection.
@ -954,15 +1307,10 @@ VT100.prototype.mouseEvent = function(event, type) {
}
// Compute mouse position in characters.
var offsetX = this.container.offsetLeft;
var offsetY = this.container.offsetTop;
for (var e = this.container; e = e.offsetParent; ) {
offsetX += e.offsetLeft;
offsetY += e.offsetTop;
}
var x = (event.clientX - offsetX) / this.cursorWidth;
var y = ((event.clientY - offsetY) + this.scrollable.offsetTop) /
this.cursorHeight - this.numScrollbackLines;
var position = this.mousePosition(event);
var x = Math.floor(position[0] / this.cursorWidth);
var y = Math.floor((position[1] + this.scrollable.scrollTop) /
this.cursorHeight) - this.numScrollbackLines;
var inside = true;
if (x >= this.terminalWidth) {
x = this.terminalWidth - 1;
@ -1022,7 +1370,7 @@ VT100.prototype.mouseEvent = function(event, type) {
// Bring up context menu.
if (button == 2 && !event.shiftKey) {
if (type == 0 /* MOUSE_DOWN */) {
this.showContextMenu(event.clientX - offsetX, event.clientY - offsetY);
this.showContextMenu(position[0], position[1]);
}
return this.cancelEvent(event);
}
@ -1058,6 +1406,29 @@ VT100.prototype.getTextContent = function(elem) {
(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) {
// Check if we find any URLs in the text. If so, automatically convert them
// to links.
@ -1103,26 +1474,7 @@ VT100.prototype.setTextContent = function(elem, s) {
return;
}
// 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;
}
}
this.setTextContentRaw(elem, s);
};
VT100.prototype.insertBlankLine = function(y, color, style) {
@ -1578,28 +1930,22 @@ VT100.prototype.enableAlternateScreen = function(state) {
this.console[this.currentScreen].style.display = '';
// Select appropriate character pitch.
var styles = [ 'transform',
'WebkitTransform',
'MozTransform',
'filter' ];
for (var i = 0; i < styles.length; ++i) {
if (typeof this.console[0].style[styles[i]] != 'undefined') {
var transform = this.getTransformName();
if (transform) {
if (state) {
// Upon enabling the alternate screen, we switch to 80 column mode. But
// upon returning to the regular screen, we restore the mode that was
// in effect previously.
this.console[1].style[styles[i]] = '';
this.console[1].style[transform] = '';
}
var style =
this.console[this.currentScreen].style[styles[i]];
this.cursor.style[styles[i]] = style;
this.space.style[styles[i]] = style;
this.console[this.currentScreen].style[transform];
this.cursor.style[transform] = style;
this.space.style[transform] = style;
this.scale = style == '' ? 1.0:1.65;
if (styles[i] == 'filter') {
if (transform == 'filter') {
this.console[this.currentScreen].style.width = style == '' ? '165%':'';
}
break;
}
}
this.resizer();
@ -1969,12 +2315,76 @@ VT100.prototype.toggleBell = function() {
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() {
this.blinkingCursor = !this.blinkingCursor;
};
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" +
"For more information check http://shellinabox.com");
};
@ -2007,6 +2417,9 @@ VT100.prototype.showContextMenu = function(x, y) {
'<li>' +
(this.visualBell ? '<img src="enabled.gif" />' : '') +
'Visual Bell</li>'+
'<li>' +
(this.softKeyboard ? '<img src="enabled.gif" />' : '') +
'Onscreen Keyboard</li>' +
'<li id="endconfig">' +
(this.blinkingCursor ? '<img src="enabled.gif" />' : '') +
'Blinking Cursor</li>'+
@ -2038,6 +2451,7 @@ VT100.prototype.showContextMenu = function(x, y) {
// Actions for default items
var actions = [ this.copyLast, p, this.reset,
this.toggleUTF, this.toggleBell,
this.toggleSoftKeyboard,
this.toggleCursorBlinking ];
// Actions for user CSS styles (if any)
@ -2093,26 +2507,30 @@ VT100.prototype.showContextMenu = function(x, y) {
}
// Position menu next to the mouse pointer
if (x + popup.clientWidth > this.container.offsetWidth) {
x = this.container.offsetWidth - popup.clientWidth;
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';
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) {
x = 0;
if (x < margin) {
x = margin;
}
if (y + popup.clientHeight > this.container.offsetHeight) {
y = this.container.offsetHeight-popup.clientHeight;
if (y + popup.clientHeight >= this.container.offsetHeight - margin) {
y = this.container.offsetHeight-popup.clientHeight - margin - 1;
}
if (y < 0) {
y = 0;
if (y < margin) {
y = margin;
}
popup.style.left = x + 'px';
popup.style.top = y + 'px';
// 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) {
return function() {
vt100.hideContextMenu();
@ -2895,33 +3313,36 @@ VT100.prototype.restoreCursor = function() {
this.savedY[this.currentScreen]);
};
VT100.prototype.set80_132Mode = function(state) {
var transform = undefined;
var styles = [ 'transform',
'WebkitTransform',
'MozTransform',
'filter'
];
VT100.prototype.getTransformName = function() {
var styles = [ 'transform', 'WebkitTransform', 'MozTransform', 'filter' ];
for (var i = 0; i < styles.length; ++i) {
if (typeof this.console[0].style[styles[i]] != 'undefined') {
transform = styles[i];
break;
return styles[i];
}
}
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 ((this.console[this.currentScreen].style[transform] != '') == state) {
return;
}
var style =
state ? transform == 'filter'
? 'progid:DXImageTransform.Microsoft.Matrix(' +
'M11=0.606060606060606060606,M12=0,M21=0,M22=1,' +
"sizingMethod='auto expand')"
: 'translateX(-50%) ' +
'scaleX(0.606060606060606060606) ' +
'translateX(50%)'
: '';
var style = state ?
this.getTransformStyle(transform, 1.65):'';
this.console[this.currentScreen].style[transform] = style;
this.cursor.style[transform] = style;
this.space.style[transform] = style;

View file

@ -238,23 +238,17 @@ VT100.prototype.reset = function(clearHistory) {
this.enableAlternateScreen(false);
var wasCompressed = false;
var styles = [ 'transform',
'WebkitTransform',
'MozTransform',
'filter' ];
for (var i = 0; i < styles.length; ++i) {
if (typeof this.console[0].style[styles[i]] != 'undefined') {
for (var j = 0; j < 1; ++j) {
wasCompressed |= this.console[j].style[styles[i]] != '';
this.console[j].style[styles[i]] = '';
var transform = this.getTransformName();
if (transform) {
for (var i = 0; i < 2; ++i) {
wasCompressed |= this.console[i].style[transform] != '';
this.console[i].style[transform] = '';
}
this.cursor.style[styles[i]] = '';
this.space.style[styles[i]] = '';
if (styles[i] == 'filter') {
this.cursor.style[transform] = '';
this.space.style[transform] = '';
if (transform == 'filter') {
this.console[this.currentScreen].style.width = '';
}
break;
}
}
this.scale = 1.0;
if (wasCompressed) {
@ -270,22 +264,26 @@ VT100.prototype.reset = function(clearHistory) {
};
VT100.prototype.addListener = function(elem, event, listener) {
try {
if (elem.addEventListener) {
elem.addEventListener(event, listener, false);
} else {
elem.attachEvent('on' + event, listener);
}
} catch (e) {
}
};
VT100.prototype.getUserSettings = function() {
// Compute hash signature to identify the entries in the userCSS menu.
// If the menu is unchanged from last time, default values can be
// looked up in a cookie associated with this page.
this.signature = 2;
this.signature = 3;
this.utfPreferred = true;
this.visualBell = typeof suppressAllAudio != 'undefined' &&
suppressAllAudio;
this.autoprint = true;
this.softKeyboard = false;
this.blinkingCursor = true;
if (this.visualBell) {
this.signature = Math.floor(16807*this.signature + 1) %
@ -311,15 +309,16 @@ VT100.prototype.getUserSettings = function() {
if (settings >= 0) {
settings = document.cookie.substr(settings + key.length).
replace(/([0-1]*).*/, "$1");
if (settings.length == 3 + (typeof userCSSList == 'undefined' ?
if (settings.length == 5 + (typeof userCSSList == 'undefined' ?
0 : userCSSList.length)) {
this.utfPreferred = settings.charAt(0) != '0';
this.visualBell = settings.charAt(1) != '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') {
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.visualBell ? '1' : '0') +
(this.autoprint ? '1' : '0') +
(this.softKeyboard ? '1' : '0') +
(this.blinkingCursor ? '1' : '0');
if (typeof userCSSList != 'undefined') {
for (var i = 0; i < userCSSList.length; ++i) {
@ -413,7 +413,7 @@ VT100.prototype.initializeUserCSSStyles = function() {
label.textContent= label.textContent;
}
// User style sheets are number sequentially
// User style sheets are numbered sequentially
var sheet = document.getElementById(
'usercss-' + i);
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) {
// If the necessary objects have not already been defined in the HTML
// page, create them now.
@ -483,6 +805,9 @@ VT100.prototype.initializeElements = function(container) {
if (!this.getChildById(this.container, 'reconnect') ||
!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, 'console') ||
!this.getChildById(this.container, 'alt_console') ||
@ -525,7 +850,15 @@ VT100.prototype.initializeElements = function(container) {
'<div id="cursize" style="visibility: hidden">' +
'</div>' +
'<div id="menu"></div>' +
'<div id="keyboard" unselectable="on">' +
KEYBOARD +
'</div>' +
'<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="console">' +
'<pre></pre>' +
@ -566,6 +899,8 @@ VT100.prototype.initializeElements = function(container) {
this.reconnectBtn = this.getChildById(this.container,'reconnect');
this.curSizeBox = this.getChildById(this.container, 'cursize');
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,
'scrollable');
this.lineheight = this.getChildById(this.container,
@ -646,6 +981,9 @@ VT100.prototype.initializeElements = function(container) {
// Hide context menu
this.hideContextMenu();
// Set up onscreen soft keyboard
this.initializeKeyboard();
// Add listener to reconnect button
this.addListener(this.reconnectBtn.firstChild, 'click',
function(vt100) {
@ -733,6 +1071,7 @@ VT100.prototype.reconnect = function() {
VT100.prototype.showReconnect = function(state) {
if (state) {
this.hideSoftKeyboard();
this.reconnectBtn.style.visibility = '';
} else {
this.reconnectBtn.style.visibility = 'hidden';
@ -766,6 +1105,9 @@ VT100.prototype.resized = function(w, h) {
};
VT100.prototype.resizer = function() {
// Hide onscreen soft keyboard
this.hideSoftKeyboard();
// The cursor can get corrupted if the print-preview is displayed in Firefox.
// Recreating it, will repair it.
var newCursor = document.createElement('pre');
@ -945,6 +1287,17 @@ VT100.prototype.cancelEvent = function(event) {
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) {
// If any text is currently selected, do not move the focus as that would
// invalidate the selection.
@ -954,15 +1307,10 @@ VT100.prototype.mouseEvent = function(event, type) {
}
// Compute mouse position in characters.
var offsetX = this.container.offsetLeft;
var offsetY = this.container.offsetTop;
for (var e = this.container; e = e.offsetParent; ) {
offsetX += e.offsetLeft;
offsetY += e.offsetTop;
}
var x = (event.clientX - offsetX) / this.cursorWidth;
var y = ((event.clientY - offsetY) + this.scrollable.offsetTop) /
this.cursorHeight - this.numScrollbackLines;
var position = this.mousePosition(event);
var x = Math.floor(position[0] / this.cursorWidth);
var y = Math.floor((position[1] + this.scrollable.scrollTop) /
this.cursorHeight) - this.numScrollbackLines;
var inside = true;
if (x >= this.terminalWidth) {
x = this.terminalWidth - 1;
@ -1022,7 +1370,7 @@ VT100.prototype.mouseEvent = function(event, type) {
// Bring up context menu.
if (button == 2 && !event.shiftKey) {
if (type == MOUSE_DOWN) {
this.showContextMenu(event.clientX - offsetX, event.clientY - offsetY);
this.showContextMenu(position[0], position[1]);
}
return this.cancelEvent(event);
}
@ -1058,6 +1406,29 @@ VT100.prototype.getTextContent = function(elem) {
(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) {
// Check if we find any URLs in the text. If so, automatically convert them
// to links.
@ -1103,26 +1474,7 @@ VT100.prototype.setTextContent = function(elem, s) {
return;
}
// 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;
}
}
this.setTextContentRaw(elem, s);
};
VT100.prototype.insertBlankLine = function(y, color, style) {
@ -1578,28 +1930,22 @@ VT100.prototype.enableAlternateScreen = function(state) {
this.console[this.currentScreen].style.display = '';
// Select appropriate character pitch.
var styles = [ 'transform',
'WebkitTransform',
'MozTransform',
'filter' ];
for (var i = 0; i < styles.length; ++i) {
if (typeof this.console[0].style[styles[i]] != 'undefined') {
var transform = this.getTransformName();
if (transform) {
if (state) {
// Upon enabling the alternate screen, we switch to 80 column mode. But
// upon returning to the regular screen, we restore the mode that was
// in effect previously.
this.console[1].style[styles[i]] = '';
this.console[1].style[transform] = '';
}
var style =
this.console[this.currentScreen].style[styles[i]];
this.cursor.style[styles[i]] = style;
this.space.style[styles[i]] = style;
this.console[this.currentScreen].style[transform];
this.cursor.style[transform] = style;
this.space.style[transform] = style;
this.scale = style == '' ? 1.0:1.65;
if (styles[i] == 'filter') {
if (transform == 'filter') {
this.console[this.currentScreen].style.width = style == '' ? '165%':'';
}
break;
}
}
this.resizer();
@ -1969,6 +2315,70 @@ VT100.prototype.toggleBell = function() {
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() {
this.blinkingCursor = !this.blinkingCursor;
};
@ -2007,6 +2417,9 @@ VT100.prototype.showContextMenu = function(x, y) {
'<li>' +
(this.visualBell ? '<img src="enabled.gif" />' : '') +
'Visual Bell</li>'+
'<li>' +
(this.softKeyboard ? '<img src="enabled.gif" />' : '') +
'Onscreen Keyboard</li>' +
'<li id="endconfig">' +
(this.blinkingCursor ? '<img src="enabled.gif" />' : '') +
'Blinking Cursor</li>'+
@ -2038,6 +2451,7 @@ VT100.prototype.showContextMenu = function(x, y) {
// Actions for default items
var actions = [ this.copyLast, p, this.reset,
this.toggleUTF, this.toggleBell,
this.toggleSoftKeyboard,
this.toggleCursorBlinking ];
// Actions for user CSS styles (if any)
@ -2093,26 +2507,30 @@ VT100.prototype.showContextMenu = function(x, y) {
}
// Position menu next to the mouse pointer
if (x + popup.clientWidth > this.container.offsetWidth) {
x = this.container.offsetWidth - popup.clientWidth;
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';
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) {
x = 0;
if (x < margin) {
x = margin;
}
if (y + popup.clientHeight > this.container.offsetHeight) {
y = this.container.offsetHeight-popup.clientHeight;
if (y + popup.clientHeight >= this.container.offsetHeight - margin) {
y = this.container.offsetHeight-popup.clientHeight - margin - 1;
}
if (y < 0) {
y = 0;
if (y < margin) {
y = margin;
}
popup.style.left = x + 'px';
popup.style.top = y + 'px';
// 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) {
return function() {
vt100.hideContextMenu();
@ -2895,33 +3313,36 @@ VT100.prototype.restoreCursor = function() {
this.savedY[this.currentScreen]);
};
VT100.prototype.set80_132Mode = function(state) {
var transform = undefined;
var styles = [ 'transform',
'WebkitTransform',
'MozTransform',
'filter'
];
VT100.prototype.getTransformName = function() {
var styles = [ 'transform', 'WebkitTransform', 'MozTransform', 'filter' ];
for (var i = 0; i < styles.length; ++i) {
if (typeof this.console[0].style[styles[i]] != 'undefined') {
transform = styles[i];
break;
return styles[i];
}
}
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 ((this.console[this.currentScreen].style[transform] != '') == state) {
return;
}
var style =
state ? transform == 'filter'
? 'progid:DXImageTransform.Microsoft.Matrix(' +
'M11=0.606060606060606060606,M12=0,M21=0,M22=1,' +
"sizingMethod='auto expand')"
: 'translateX(-50%) ' +
'scaleX(0.606060606060606060606) ' +
'translateX(50%)'
: '';
var style = state ?
this.getTransformStyle(transform, 1.65):'';
this.console[this.currentScreen].style[transform] = style;
this.cursor.style[transform] = style;
this.space.style[transform] = style;