added virtual keyboard

This commit is contained in:
lars 2008-06-20 07:56:40 +00:00
parent f8526df99d
commit 7d02a50eaa
22 changed files with 6551 additions and 3 deletions

View file

@ -110,6 +110,12 @@ h2 {
padding: 20px;
}
#vkb {
margin: 0 auto 0 15%;
}
/* Style for the footer */
#footer {
padding: 2px;

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View file

@ -0,0 +1,335 @@
html.VirtualKeyboardPopup, body.VirtualKeyboardPopup {
padding: 0;
margin: 0;
}
#virtualKeyboard {
border: 1px solid #686888;
background: #fefefe;
height: 160px;
margin: 0;
padding: 0;
position: relative;
width: 396px;
}
/*
* global overrides
*/
#virtualKeyboard * {
border: 0;
color: black;
cursor: default;
margin: 0;
padding: 0;
}
#virtualKeyboard #kbDesk {
display: inline-block;
margin: 0;
padding: 4px 2px 0 2px;
/*IE5.5 will ignore the rule below */
margin/**/:/**/ 4px 2px 0 4px;
padding/**/:/**/ 0;
/*--*/
position: relative;
font-size: 1px;
overflow: hidden;
-moz-user-select: none;
-khtml-user-select: none;
}
#virtualKeyboard #kbDesk div.kbButton {
float: left;
height: 26px;
overflow: hidden;
padding: 0;
position: relative;
width: 26px;
z-index: 2;
}
#virtualKeyboard #kbDesk div.kbButton a {
background: url(button_set.gif) 0 0 no-repeat;
display: block;
height: 100%;
position: relative;
text-decoration: none;
width: 100%;
}
#virtualKeyboard #kbDesk.capsLock div.kbButton span.normal {
text-transform: uppercase;
}
#virtualKeyboard #kbDesk div.kbButtonHover a {
background-position: 0 -27px;
}
#virtualKeyboard #kbDesk div.kbButtonDown a {
background-position: 0 -54px;
}
#virtualKeyboard #kbDesk div.kbButton span {
display: block;
font-family: Verdana;
font-size: 13px;
font-weight: normal;
overflow: visible;
text-align: center;
}
#virtualKeyboard #kbDesk div.kbButton span.shifted,
#virtualKeyboard #kbDesk div.kbButton span.alted {
color: green;
font-size: 6pt;
height: 100%;
line-height: 1.1;
position: absolute;
right: -5px;
top: 0;
text-transform: none;
vertical-align: bottom;
width: 100%;
}
#virtualKeyboard #kbDesk div.kbButton span.alted {
color: blue;
right: 7px;
}
#virtualKeyboard #kbDesk div.kbButton span.normal {
position: absolute;
bottom: 4px;
left: 5px;
text-indent: 0;
}
#virtualKeyboard #kbDesk span.deadKey {
color: red;
}
#virtualKeyboard #kbDesk div#kb_benter {
background-position: -194px 0px;
margin-top: -26px;
position: relative;
float: right;
height: 52px;
width: 56px;
z-index: -1;
}
#virtualKeyboard #kbDesk div#kb_benter[id] {
z-index: 0;
}
#virtualKeyboard #kbDesk div#kb_benter a {
background-position: -194px 0px;
}
#virtualKeyboard #kbDesk div#kb_benter.kbButtonHover a {
background-position: -194px -53px;
}
#virtualKeyboard #kbDesk div#kb_benter.kbButtonDown a {
background-position: -194px -106px;
}
#virtualKeyboard #kbDesk div#kb_bbackspace a {
background-position: -27px -81px;
}
#virtualKeyboard #kbDesk div#kb_bbackspace.kbButtonHover a {
background-position: -27px -108px;
}
#virtualKeyboard #kbDesk div#kb_bbackspace.kbButtonDown a {
background-position: -27px -135px;
}
#virtualKeyboard #kbDesk div#kb_btab {
width: 41px;
}
#virtualKeyboard #kbDesk div#kb_btab a {
background-position: -155px -81px;
}
#virtualKeyboard #kbDesk div#kb_btab.kbButtonHover a {
background-position: -155px -108px;
}
#virtualKeyboard #kbDesk div#kb_btab.kbButtonDown a {
background-position: -155px -135px;
}
#virtualKeyboard #kbDesk div#kb_bcaps {
width: 48px;
}
#virtualKeyboard #kbDesk div#kb_bcaps a {
background-position: -107px -81px;
}
#virtualKeyboard #kbDesk div#kb_bcaps.kbButtonHover a {
background-position: -107px -108px;
}
#virtualKeyboard #kbDesk div#kb_bcaps.kbButtonDown a {
background-position: -107px -135px;
}
#virtualKeyboard #kbDesk div#kb_bshift_left,
#virtualKeyboard #kbDesk div#kb_bshift_right {
width: 52px;
}
#virtualKeyboard #kbDesk div#kb_bshift_left a,
#virtualKeyboard #kbDesk div#kb_bshift_right a {
background-position: -54px -81px;
}
#virtualKeyboard #kbDesk div#kb_bshift_left.kbButtonHover a,
#virtualKeyboard #kbDesk div#kb_bshift_right.kbButtonHover a {
background-position: -54px -108px;
}
#virtualKeyboard #kbDesk div#kb_bshift_left.kbButtonDown a,
#virtualKeyboard #kbDesk div#kb_bshift_right.kbButtonDown a {
background-position: -54px -135px;
}
#virtualKeyboard #kbDesk div#kb_balt_left,
#virtualKeyboard #kbDesk div#kb_balt_right {
padding: 0 0 0 2px;
width: 32px;
}
#virtualKeyboard #kbDesk div#kb_balt_left {
margin-left: 33px;
padding-left: 0;
/*IE5.5 will ignore the rule below */
padding-left/**/:/**/ 41px;
margin-left/**/:/**/ 0;
/*--*/
}
#virtualKeyboard #kbDesk div#kb_balt_left a,
#virtualKeyboard #kbDesk div#kb_balt_right a {
background-position: -251px 0;
}
#virtualKeyboard #kbDesk div#kb_balt_left.kbButtonHover a,
#virtualKeyboard #kbDesk div#kb_balt_right.kbButtonHover a {
background-position: -251px -27px;
}
#virtualKeyboard #kbDesk div#kb_balt_left.kbButtonDown a,
#virtualKeyboard #kbDesk div#kb_balt_right.kbButtonDown a {
background-position: -251px -54px;
}
#virtualKeyboard #kbDesk div#kb_bctrl_left,
#virtualKeyboard #kbDesk div#kb_bctrl_right {
float: left;
width: 32px;
}
#virtualKeyboard #kbDesk div#kb_bctrl_right {
float: right;
}
#virtualKeyboard #kbDesk div#kb_bctrl_left a,
#virtualKeyboard #kbDesk div#kb_bctrl_right a {
background-position: -251px -81px;
}
#virtualKeyboard #kbDesk div#kb_bctrl_left.kbButtonHover a,
#virtualKeyboard #kbDesk div#kb_bctrl_right.kbButtonHover a {
background-position: -251px -108px;
}
#virtualKeyboard #kbDesk div#kb_bctrl_left.kbButtonDown a,
#virtualKeyboard #kbDesk div#kb_bctrl_right.kbButtonDown a {
background-position: -251px -135px;
}
#virtualKeyboard #kbDesk div#kb_bdel a {
background-position: 0 -81px;
}
#virtualKeyboard #kbDesk div#kb_bdel.kbButtonHover a {
background-position: 0 -108px;
}
#virtualKeyboard #kbDesk div#kb_bdel.kbButtonDown a {
background-position: 0 -135px;
}
#virtualKeyboard #kbDesk div#kb_bspace {
width: 166px;
}
#virtualKeyboard #kbDesk div#kb_bspace a {
background-position: -27px 0;
}
#virtualKeyboard #kbDesk div#kb_bspace.kbButtonHover a {
background-position: -27px -27px;
}
#virtualKeyboard #kbDesk div#kb_bspace.kbButtonDown a {
background-position: -27px -54px;
}
#virtualKeyboard select#kb_langselector,
#virtualKeyboard select#kb_mappingselector {
border: 1px solid black;
bottom: 2px;
position: absolute;
right: 2px;
width: 125px;
}
#virtualKeyboard select#kb_mappingselector {
right: 140px;
}
#virtualKeyboard select,
#virtualKeyboard select option {
background: #fff;
font-family: Arial, Tahoma, Verdana sans-serif;
font-size: 11px;
}
#virtualKeyboard select optgroup option {
padding-left: 20px;
}
#virtualKeyboard #copyrights {
bottom: 4px;
color: blue;
font-size: 9px;
left: 2px;
line-height: normal;
position: absolute;
}
#virtualKeyboard #copyrights a {
font-size: 9px;
color: blue;
cursor: default;
outline: 0;
}
/**
*
* Styles for the IME field
*
*/
#VirtualKeyboardIME {
background: #fff;
border:1px solid #333;
position: absolute;
width: 200px;
}
#VirtualKeyboardIME div.IMEContent {
border: 1px solid #333;
border-top: 0;
border-bottom: 0;
height: 21px;
line-height: 21px;
margin: 0 12px;
overflow: auto;
padding: 0 3px;
white-space: nowrap;
}
#VirtualKeyboardIME div.left {
border-bottom: 10px solid #fff;
border-left: 0px solid black;
border-right: 10px solid #000;
border-top: 10px solid #fff;
position: absolute;
left: 1px;
font-size: 1px;
overflow: hidden;
}
#VirtualKeyboardIME div.right {
border-bottom: 10px solid #fff;
border-left: 10px solid #000;
border-right: 0px solid black;
border-top: 10px solid #fff;
float: right;
position: absolute;
right: 1px;
overflow: hidden;
}
/************************************
* Place for the locale-dependent styles
* overload fonts here
*
* Language-dependent class name is equal to uppercased language domain code (ZH in zh-CN)
*/
#virtualKeyboard #kbDesk.ZH div.kbButton span {
font-family: MingLiU, SimSun, "Arial Unicode MS";
font-size: 13px;
}
#virtualKeyboard #kbDesk.ZH div.kbButton span.alted,
#virtualKeyboard #kbDesk.ZH div.kbButton span.shifted {
font-size: 9px;
}
#VirtualKeyboardIME.ZH div.IMEContent {
font-family: SimSun, "Arial Unicode MS";
font-size: 16px;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

View file

@ -0,0 +1,307 @@
html.VirtualKeyboardPopup, body.VirtualKeyboardPopup {
padding: 0;
margin: 0;
}
#virtualKeyboard {
border: 1px solid #686888;
background: #f0ecff;
height: 120px;
margin: 0;
padding: 0;
position: relative;
width: 276px;
}
/*
* global overrides
*/
#virtualKeyboard * {
border: 0;
color: black;
cursor: default;
margin: 0;
padding: 0;
}
#virtualKeyboard #kbDesk {
display: inline-block;
margin: 0;
padding: 4px 2px 0 2px;
/*IE5.5 will ignore the rule below */
margin/**/:/**/ 4px 2px 0 4px;
padding/**/:/**/ 0;
/*--*/
position: relative;
font-size: 1px;
overflow: hidden;
-moz-user-select: none;
-khtml-user-select: none;
}
#virtualKeyboard #kbDesk div.kbButton {
float: left;
height: 18px;
overflow: hidden;
padding: 0;
position: relative;
width: 18px;
z-index: 2;
}
#virtualKeyboard #kbDesk div.kbButton a {
background: url(button_set.gif) 0 0 no-repeat;
display: block;
height: 100%;
position: relative;
text-decoration: none;
width: 100%;
}
#virtualKeyboard #kbDesk.capsLock div.kbButton span.normal {
text-transform: uppercase;
}
#virtualKeyboard #kbDesk div.kbButtonHover a {
background-position: 0 -21px;
}
#virtualKeyboard #kbDesk div.kbButtonDown a {
background-position: 0 -42px;
}
#virtualKeyboard #kbDesk div.kbButton span {
display: block;
font-family: Verdana;
font-size: 11px;
font-weight: normal;
overflow: visible;
position: relative;
text-align: center;
top: 2px;
}
#virtualKeyboard #kbDesk div.kbButton span.shifted,
#virtualKeyboard #kbDesk div.kbButton span.alted {
display: none;
}
#virtualKeyboard #kbDesk span.deadKey {
color: red;
}
#virtualKeyboard #kbDesk div#kb_benter {
margin-top: -18px;
position: relative;
float: right;
height: 36px;
width: 38px;
z-index: -1;
}
#virtualKeyboard #kbDesk div#kb_benter[id] {
z-index: 0;
}
#virtualKeyboard #kbDesk div#kb_benter a {
background-position: -150px 0;
}
#virtualKeyboard #kbDesk div#kb_benter.kbButtonHover a {
background-position: -150px -42px;
}
#virtualKeyboard #kbDesk div#kb_benter.kbButtonDown a {
background-position: -150px -84px;
}
#virtualKeyboard #kbDesk div#kb_bbackspace a {
background-position: -21px -63px;
}
#virtualKeyboard #kbDesk div#kb_bbackspace.kbButtonHover a {
background-position: -21px -84px;
}
#virtualKeyboard #kbDesk div#kb_bbackspace.kbButtonDown a {
background-position: -21px -105px;
}
#virtualKeyboard #kbDesk div#kb_btab {
width: 29px;
}
#virtualKeyboard #kbDesk div#kb_btab a {
background-position: -117px -63px;
}
#virtualKeyboard #kbDesk div#kb_btab.kbButtonHover a {
background-position: -117px -84px;
}
#virtualKeyboard #kbDesk div#kb_btab.kbButtonDown a {
background-position: -117px -105px;
}
#virtualKeyboard #kbDesk div#kb_bcaps {
width: 33px;
}
#virtualKeyboard #kbDesk div#kb_bcaps a {
background-position: -81px -63px;
}
#virtualKeyboard #kbDesk div#kb_bcaps.kbButtonHover a {
background-position: -81px -84px;
}
#virtualKeyboard #kbDesk div#kb_bcaps.kbButtonDown a {
background-position: -81px -105px;
}
#virtualKeyboard #kbDesk div#kb_bshift_left,
#virtualKeyboard #kbDesk div#kb_bshift_right {
width: 36px;
}
#virtualKeyboard #kbDesk div#kb_bshift_left a,
#virtualKeyboard #kbDesk div#kb_bshift_right a {
background-position: -42px -63px;
}
#virtualKeyboard #kbDesk div#kb_bshift_left.kbButtonHover a,
#virtualKeyboard #kbDesk div#kb_bshift_right.kbButtonHover a {
background-position: -42px -84px;
}
#virtualKeyboard #kbDesk div#kb_bshift_left.kbButtonDown a,
#virtualKeyboard #kbDesk div#kb_bshift_right.kbButtonDown a {
background-position: -42px -105px;
}
#virtualKeyboard #kbDesk div#kb_balt_left,
#virtualKeyboard #kbDesk div#kb_balt_right {
width: 21px;
}
#virtualKeyboard #kbDesk div#kb_balt_left {
margin-left: -23px;
padding-left: 0;
/*IE5.5 will ignore the rule below */
padding-left/**/:/**/ 32px;
margin-left/**/:/**/ 0;
/*--*/
}
#virtualKeyboard #kbDesk div#kb_balt_left a,
#virtualKeyboard #kbDesk div#kb_balt_right a {
background-position: -192px 0;
}
#virtualKeyboard #kbDesk div#kb_balt_left.kbButtonHover a,
#virtualKeyboard #kbDesk div#kb_balt_right.kbButtonHover a {
background-position: -192px -21px;
}
#virtualKeyboard #kbDesk div#kb_balt_left.kbButtonDown a,
#virtualKeyboard #kbDesk div#kb_balt_right.kbButtonDown a {
background-position: -192px -42px;
}
#virtualKeyboard #kbDesk div#kb_bctrl_left,
#virtualKeyboard #kbDesk div#kb_bctrl_right {
float: left;
width: 29px;
}
#virtualKeyboard #kbDesk div#kb_bctrl_right {
float: right;
}
#virtualKeyboard #kbDesk div#kb_bctrl_left a,
#virtualKeyboard #kbDesk div#kb_bctrl_right a {
background-position: -191px -63px;
}
#virtualKeyboard #kbDesk div#kb_bctrl_left.kbButtonHover a,
#virtualKeyboard #kbDesk div#kb_bctrl_right.kbButtonHover a {
background-position: -191px -84px;
}
#virtualKeyboard #kbDesk div#kb_bctrl_left.kbButtonDown a,
#virtualKeyboard #kbDesk div#kb_bctrl_right.kbButtonDown a {
background-position: -191px -105px;
}
#virtualKeyboard #kbDesk div#kb_bdel a {
background-position: 0 -63px;
}
#virtualKeyboard #kbDesk div#kb_bdel.kbButtonHover a {
background-position: 0 -84px;
}
#virtualKeyboard #kbDesk div#kb_bdel.kbButtonDown a {
background-position: 0 -105px;
}
#virtualKeyboard #kbDesk div#kb_bspace {
width: 100px;
}
#virtualKeyboard #kbDesk div#kb_bspace a {
background-position: -21px 0;
}
#virtualKeyboard #kbDesk div#kb_bspace.kbButtonHover a {
background-position: -21px -21px;
}
#virtualKeyboard #kbDesk div#kb_bspace.kbButtonDown a {
background-position: -21px -42px;
}
#virtualKeyboard select#kb_langselector,
#virtualKeyboard select#kb_mappingselector {
border: 1px solid black;
bottom: 2px;
position: absolute;
right: 2px;
width: 75px;
}
#virtualKeyboard select#kb_mappingselector {
right: 80px;
}
#virtualKeyboard select,
#virtualKeyboard select option {
background: #fff;
font-family: Arial, Tahoma, Verdana sans-serif;
font-size: 11px;
}
#virtualKeyboard select optgroup option {
padding-left: 20px;
}
#virtualKeyboard #copyrights {
bottom: 2px;
color: #77a;
font-size: 9px;
left: 4px;
line-height: normal;
position: absolute;
}
#virtualKeyboard #copyrights a {
font-size: 9px;
color: #77a;
cursor: default;
outline: 0;
}
/**
*
* Styles for the IME field
*
*/
#VirtualKeyboardIME {
background: #fff;
border:1px solid #333;
position: absolute;
width: 200px;
}
#VirtualKeyboardIME div.IMEContent {
border: 1px solid #333;
border-top: 0;
border-bottom: 0;
height: 21px;
line-height: 21px;
margin: 0 12px;
overflow: auto;
padding: 0 3px;
white-space: nowrap;
}
#VirtualKeyboardIME div.left {
border-bottom: 10px solid #fff;
border-left: 0px solid black;
border-right: 10px solid #000;
border-top: 10px solid #fff;
position: absolute;
left: 1px;
font-size: 1px;
overflow: hidden;
}
#VirtualKeyboardIME div.right {
border-bottom: 10px solid #fff;
border-left: 10px solid #000;
border-right: 0px solid black;
border-top: 10px solid #fff;
float: right;
position: absolute;
right: 1px;
overflow: hidden;
}
/************************************
* Place for the locale-dependent styles
* overload fonts here
*
* Language-dependent class name is equal to uppercased language domain code (ZH in zh-CN)
*/
#virtualKeyboard #kbDesk.ZH div.kbButton span {
font-family: MingLiU, SimSun, "Arial Unicode MS";
font-size: 11px;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6 KiB

View file

@ -0,0 +1,342 @@
/* Skin made by Bastiaan Fronik (info [ at ] bastiaanfronik dot com)
based on stylesheet of WingedFox */
html.VirtualKeyboardPopup, body.VirtualKeyboardPopup {
padding: 0;
margin: 0;
}
#virtualKeyboard {
border: 1px solid #686888;
background: #dedfe0;
height: 242px;
margin: 0;
padding-right: 2px;
position: relative;
width: 606px;
}
/*
* global overrides
*/
#virtualKeyboard * {
border: 0;
color: black;
cursor: default;
margin: 0;
padding: 0;
}
#virtualKeyboard #kbDesk {
display: inline-block;
margin: 0;
padding: 4px 2px 0 2px;
/*IE5.5 will ignore the rule below */
margin/**/:/**/ 4px 2px 0 4px;
padding/**/:/**/ 0;
/*--*/
position: relative;
font-size: 1px;
overflow: hidden;
-moz-user-select: none;
-khtml-user-select: none;
}
#virtualKeyboard #kbDesk div.kbButton {
float: left;
height: 38px;
overflow: hidden;
padding: 0;
margin: 1px;
position: relative;
width: 38px;
z-index: 2;
}
#virtualKeyboard #kbDesk div.kbButton a {
background: url(button_set.gif) 0 0 no-repeat;
display: block;
height: 100%;
position: relative;
text-decoration: none;
width: 100%;
}
#virtualKeyboard #kbDesk.capsLock div.kbButton span.normal {
text-transform: uppercase;
}
#virtualKeyboard #kbDesk div.kbButtonHover a {
background-position: 0 -41px;
}
#virtualKeyboard #kbDesk div.kbButtonDown a {
background-position: 0 -82px;
}
#virtualKeyboard #kbDesk div.kbButton span {
display: block;
font-family: Verdana;
font-size: 18px;
font-weight: normal;
overflow: visible;
text-align: center;
}
#virtualKeyboard #kbDesk div.kbButton span.shifted,
#virtualKeyboard #kbDesk div.kbButton span.alted {
color: green;
font-size: 9pt;
height: 100%;
line-height: 1.1;
position: absolute;
right: -5px;
top: 0;
text-transform: none;
vertical-align: bottom;
width: 100%;
}
#virtualKeyboard #kbDesk div.kbButton span.alted {
color: blue;
right: 7px;
}
#virtualKeyboard #kbDesk div.kbButton span.normal {
position: absolute;
bottom: 4px;
left: 5px;
text-indent: 0;
}
#virtualKeyboard #kbDesk span.deadKey {
color: red;
}
#virtualKeyboard #kbDesk div#kb_benter
{
background-position: -303px 0px;
margin-top: -39px;
margin-bottom: 1px;
position: relative;
float: right;
height: 78px;
width: 85px;
z-index: -1;
}
#virtualKeyboard #kbDesk div#kb_benter[id] {
z-index: 0;
}
#virtualKeyboard #kbDesk div#kb_benter a {
background-position: -303px 0px;
}
#virtualKeyboard #kbDesk div#kb_benter.kbButtonHover a {
background-position: -303px -81px;
}
#virtualKeyboard #kbDesk div#kb_benter.kbButtonDown a {
background-position: -303px -162px;
}
#virtualKeyboard #kbDesk div#kb_bbackspace a {
background-position: -41px -123px;
}
#virtualKeyboard #kbDesk div#kb_bbackspace.kbButtonHover a {
background-position: -41px -164px;
}
#virtualKeyboard #kbDesk div#kb_bbackspace.kbButtonDown a {
background-position: -41px -205px;
}
#virtualKeyboard #kbDesk div#kb_btab {
width: 60px;
}
#virtualKeyboard #kbDesk div#kb_btab a {
background-position: -238px -123px;
}
#virtualKeyboard #kbDesk div#kb_btab.kbButtonHover a {
background-position: -238px -164px;
}
#virtualKeyboard #kbDesk div#kb_btab.kbButtonDown a {
background-position: -238px -205px;
}
#virtualKeyboard #kbDesk div#kb_bcaps {
width: 71px;
}
#virtualKeyboard #kbDesk div#kb_bcaps a {
background-position: -164px -123px;
}
#virtualKeyboard #kbDesk div#kb_bcaps.kbButtonHover a {
background-position: -164px -164px;
}
#virtualKeyboard #kbDesk div#kb_bcaps.kbButtonDown a {
background-position: -164px -205px;
}
#virtualKeyboard #kbDesk div#kb_bshift_left,
#virtualKeyboard #kbDesk div#kb_bshift_right {
width: 78px;
}
#virtualKeyboard #kbDesk div#kb_bshift_left a,
#virtualKeyboard #kbDesk div#kb_bshift_right a {
background-position: -82px -123px;
}
#virtualKeyboard #kbDesk div#kb_bshift_left.kbButtonHover a,
#virtualKeyboard #kbDesk div#kb_bshift_right.kbButtonHover a {
background-position: -82px -164px;
}
#virtualKeyboard #kbDesk div#kb_bshift_left.kbButtonDown a,
#virtualKeyboard #kbDesk div#kb_bshift_right.kbButtonDown a {
background-position: -82px -205px;
}
#virtualKeyboard #kbDesk div#kb_balt_left,
#virtualKeyboard #kbDesk div#kb_balt_right {
padding: 0 0 0 2px;
width: 47px;
}
#virtualKeyboard #kbDesk div#kb_balt_left {
margin-left: 33px;
padding-left: 0;
/*IE5.5 will ignore the rule below */
padding-left/**/:/**/ 41px;
margin-left/**/:/**/ 0;
/*--*/
}
#virtualKeyboard #kbDesk div#kb_balt_left a,
#virtualKeyboard #kbDesk div#kb_balt_right a {
background-position: -391px 0;
}
#virtualKeyboard #kbDesk div#kb_balt_left.kbButtonHover a,
#virtualKeyboard #kbDesk div#kb_balt_right.kbButtonHover a {
background-position: -391px -41px;
}
#virtualKeyboard #kbDesk div#kb_balt_left.kbButtonDown a,
#virtualKeyboard #kbDesk div#kb_balt_right.kbButtonDown a {
background-position: -391px -82px;
}
#virtualKeyboard #kbDesk div#kb_bctrl_left,
#virtualKeyboard #kbDesk div#kb_bctrl_right {
float: left;
width: 48px;
}
#virtualKeyboard #kbDesk div#kb_bctrl_right {
float: right;
}
#virtualKeyboard #kbDesk div#kb_bctrl_left a,
#virtualKeyboard #kbDesk div#kb_bctrl_right a {
background-position: -391px -123px;
}
#virtualKeyboard #kbDesk div#kb_bctrl_left.kbButtonHover a,
#virtualKeyboard #kbDesk div#kb_bctrl_right.kbButtonHover a {
background-position: -391px -164px;
}
#virtualKeyboard #kbDesk div#kb_bctrl_left.kbButtonDown a,
#virtualKeyboard #kbDesk div#kb_bctrl_right.kbButtonDown a {
background-position: -391px -205px;
}
#virtualKeyboard #kbDesk div#kb_bdel a {
background-position: 0 -123px;
}
#virtualKeyboard #kbDesk div#kb_bdel.kbButtonHover a {
background-position: 0 -164px;
}
#virtualKeyboard #kbDesk div#kb_bdel.kbButtonDown a {
background-position: 0 -205px;
}
#virtualKeyboard #kbDesk div#kb_bspace {
width: 259px;
}
#virtualKeyboard #kbDesk div#kb_bspace a {
background-position: -41px 0;
}
#virtualKeyboard #kbDesk div#kb_bspace.kbButtonHover a {
background-position: -41px -41px;
}
#virtualKeyboard #kbDesk div#kb_bspace.kbButtonDown a {
background-position: -41px -82px;
}
#virtualKeyboard select#kb_langselector,
#virtualKeyboard select#kb_mappingselector {
border: 1px solid black;
bottom: 2px;
position: absolute;
right: 2px;
width: 125px;
}
#virtualKeyboard select#kb_mappingselector {
right: 140px;
}
#virtualKeyboard select,
#virtualKeyboard select option {
background: #fff;
font-family: Arial, Tahoma, Verdana sans-serif;
font-size: 11px;
}
#virtualKeyboard select optgroup option {
padding-left: 20px;
}
#virtualKeyboard #copyrights
{
bottom: 4px;
color: #6f737a;
font-size: 9px;
left: 4px;
line-height: normal;
position: absolute;
}
#virtualKeyboard #copyrights a {
font-size: 9px;
color: #6f737a;
cursor: default;
outline: 0;
}
/**
*
* Styles for the IME field
*
*/
#VirtualKeyboardIME {
background: #fff;
border:1px solid #333;
position: absolute;
width: 200px;
}
#VirtualKeyboardIME div.IMEContent {
border: 1px solid #333;
border-top: 0;
border-bottom: 0;
height: 21px;
line-height: 21px;
margin: 0 12px;
overflow: auto;
padding: 0 3px;
white-space: nowrap;
}
#VirtualKeyboardIME div.left {
border-bottom: 10px solid #fff;
border-left: 0px solid black;
border-right: 10px solid #000;
border-top: 10px solid #fff;
position: absolute;
left: 1px;
font-size: 1px;
overflow: hidden;
}
#VirtualKeyboardIME div.right {
border-bottom: 10px solid #fff;
border-left: 10px solid #000;
border-right: 0px solid black;
border-top: 10px solid #fff;
float: right;
position: absolute;
right: 1px;
overflow: hidden;
}
/************************************
* Place for the locale-dependent styles
* overload fonts here
*
* Language-dependend class name is equal to uppercased language domain code (ZH in zh-CN)
*/
#virtualKeyboard #kbDesk.ZH div.kbButton span {
font-family: MingLiU, SimSun, "Arial Unicode MS";
font-size: 13px;
}
#virtualKeyboard #kbDesk.ZH div.kbButton span.alted,
#virtualKeyboard #kbDesk.ZH div.kbButton span.shifted {
font-size: 9px;
}
#VirtualKeyboardIME.ZH div.IMEContent {
font-family: SimSun, "Arial Unicode MS";
font-size: 16px;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View file

@ -0,0 +1,338 @@
html.VirtualKeyboardPopup, body.VirtualKeyboardPopup {
padding: 0;
margin: 0;
}
#virtualKeyboard {
border: 1px solid #686888;
background: #ECE9D8;
height: 160px;
margin: 0;
padding: 0;
position: relative;
width: 396px;
}
/*
* global overrides
*/
#virtualKeyboard * {
border: 0;
color: black;
cursor: default;
margin: 0;
padding: 0;
}
#virtualKeyboard #kbDesk {
display: inline-block;
margin: 0;
padding: 4px 2px 0 2px;
/*IE5.5 will ignore the rule below */
margin/**/:/**/ 4px 2px 0 4px;
padding/**/:/**/ 0;
/*--*/
position: relative;
font-size: 1px;
overflow: hidden;
-moz-user-select: none;
-khtml-user-select: none;
}
#virtualKeyboard #kbDesk div.kbButton {
float: left;
height: 26px;
overflow: hidden;
padding: 0;
position: relative;
width: 26px;
z-index: 2;
}
#virtualKeyboard #kbDesk div.kbButton a {
background: url(button_set.gif) 0 0 no-repeat;
display: block;
height: 100%;
position: relative;
text-decoration: none;
width: 100%;
}
#virtualKeyboard #kbDesk.capsLock div.kbButton span.normal {
text-transform: uppercase;
}
#virtualKeyboard #kbDesk div.kbButtonHover a {
background-position: 0 -27px;
}
#virtualKeyboard #kbDesk div.kbButtonDown a {
background-position: 0 -54px;
}
#virtualKeyboard #kbDesk div.kbButton span {
display: block;
font-family: Verdana;
font-size: 13px;
font-weight: normal;
overflow: visible;
text-align: center;
}
#virtualKeyboard #kbDesk div.kbButton span.shifted,
#virtualKeyboard #kbDesk div.kbButton span.alted {
color: green;
font-size: 6pt;
height: 100%;
line-height: 1.1;
position: absolute;
right: -5px;
top: 0;
text-transform: none;
vertical-align: bottom;
width: 100%;
}
#virtualKeyboard #kbDesk div.kbButton span.alted {
color: blue;
right: 7px;
}
#virtualKeyboard #kbDesk div.kbButton span.normal {
position: absolute;
bottom: 4px;
left: 5px;
text-indent: 0;
}
#virtualKeyboard #kbDesk span.deadKey {
color: red;
}
#virtualKeyboard #kbDesk div#kb_benter {
background-position: -194px 0px;
margin-top: -26px;
position: relative;
float: right;
height: 52px;
width: 56px;
z-index: -1;
}
#virtualKeyboard #kbDesk div#kb_benter[id] {
z-index: 0;
}
#virtualKeyboard #kbDesk div#kb_benter a {
background-position: -194px 0px;
}
#virtualKeyboard #kbDesk div#kb_benter.kbButtonHover a {
background-position: -194px -53px;
}
#virtualKeyboard #kbDesk div#kb_benter.kbButtonDown a {
background-position: -194px -106px;
}
#virtualKeyboard #kbDesk div#kb_bbackspace a {
background-position: -27px -81px;
}
#virtualKeyboard #kbDesk div#kb_bbackspace.kbButtonHover a {
background-position: -27px -108px;
}
#virtualKeyboard #kbDesk div#kb_bbackspace.kbButtonDown a {
background-position: -27px -135px;
}
#virtualKeyboard #kbDesk div#kb_btab {
width: 41px;
}
#virtualKeyboard #kbDesk div#kb_btab a {
background-position: -155px -81px;
}
#virtualKeyboard #kbDesk div#kb_btab.kbButtonHover a {
background-position: -155px -108px;
}
#virtualKeyboard #kbDesk div#kb_btab.kbButtonDown a {
background-position: -155px -135px;
}
#virtualKeyboard #kbDesk div#kb_bcaps {
width: 48px;
}
#virtualKeyboard #kbDesk div#kb_bcaps a {
background-position: -107px -81px;
}
#virtualKeyboard #kbDesk div#kb_bcaps.kbButtonHover a {
background-position: -107px -108px;
}
#virtualKeyboard #kbDesk div#kb_bcaps.kbButtonDown a {
background-position: -107px -135px;
}
#virtualKeyboard #kbDesk div#kb_bshift_left,
#virtualKeyboard #kbDesk div#kb_bshift_right {
width: 52px;
}
#virtualKeyboard #kbDesk div#kb_bshift_left a,
#virtualKeyboard #kbDesk div#kb_bshift_right a {
background-position: -54px -81px;
}
#virtualKeyboard #kbDesk div#kb_bshift_left.kbButtonHover a,
#virtualKeyboard #kbDesk div#kb_bshift_right.kbButtonHover a {
background-position: -54px -108px;
}
#virtualKeyboard #kbDesk div#kb_bshift_left.kbButtonDown a,
#virtualKeyboard #kbDesk div#kb_bshift_right.kbButtonDown a {
background-position: -54px -135px;
}
#virtualKeyboard #kbDesk div#kb_balt_left,
#virtualKeyboard #kbDesk div#kb_balt_right {
padding: 0 0 0 2px;
width: 32px;
}
#virtualKeyboard #kbDesk div#kb_balt_left {
margin-left: 33px;
padding-left: 0;
/*IE5.5 will ignore the rule below */
padding-left/**/:/**/ 41px;
margin-left/**/:/**/ 0;
/*--*/
}
#virtualKeyboard #kbDesk div#kb_balt_left a,
#virtualKeyboard #kbDesk div#kb_balt_right a {
background-position: -251px 0;
}
#virtualKeyboard #kbDesk div#kb_balt_left.kbButtonHover a,
#virtualKeyboard #kbDesk div#kb_balt_right.kbButtonHover a {
background-position: -251px -27px;
}
#virtualKeyboard #kbDesk div#kb_balt_left.kbButtonDown a,
#virtualKeyboard #kbDesk div#kb_balt_right.kbButtonDown a {
background-position: -251px -54px;
}
#virtualKeyboard #kbDesk div#kb_bctrl_left,
#virtualKeyboard #kbDesk div#kb_bctrl_right {
float: left;
width: 32px;
}
#virtualKeyboard #kbDesk div#kb_bctrl_right {
float: right;
}
#virtualKeyboard #kbDesk div#kb_bctrl_left a,
#virtualKeyboard #kbDesk div#kb_bctrl_right a {
background-position: -251px -81px;
}
#virtualKeyboard #kbDesk div#kb_bctrl_left.kbButtonHover a,
#virtualKeyboard #kbDesk div#kb_bctrl_right.kbButtonHover a {
background-position: -251px -108px;
}
#virtualKeyboard #kbDesk div#kb_bctrl_left.kbButtonDown a,
#virtualKeyboard #kbDesk div#kb_bctrl_right.kbButtonDown a {
background-position: -251px -135px;
}
#virtualKeyboard #kbDesk div#kb_bdel a {
background-position: 0 -81px;
}
#virtualKeyboard #kbDesk div#kb_bdel.kbButtonHover a {
background-position: 0 -108px;
}
#virtualKeyboard #kbDesk div#kb_bdel.kbButtonDown a {
background-position: 0 -135px;
}
#virtualKeyboard #kbDesk div#kb_bspace {
width: 166px;
}
#virtualKeyboard #kbDesk div#kb_bspace a {
background-position: -27px 0;
}
#virtualKeyboard #kbDesk div#kb_bspace.kbButtonHover a {
background-position: -27px -27px;
}
#virtualKeyboard #kbDesk div#kb_bspace.kbButtonDown a {
background-position: -27px -54px;
}
#virtualKeyboard select#kb_langselector,
#virtualKeyboard select#kb_mappingselector {
border: 1px solid black;
bottom: 2px;
position: absolute;
right: 2px;
width: 125px;
}
#virtualKeyboard select#kb_mappingselector {
right: 140px;
}
#virtualKeyboard select,
#virtualKeyboard select option {
background: #fff;
font-family: Arial, Tahoma, Verdana sans-serif;
font-size: 11px;
}
#virtualKeyboard select optgroup option {
padding-left: 20px;
}
#virtualKeyboard #copyrights {
bottom: 4px;
color: blue;
font-size: 9px;
left: 2px;
line-height: normal;
position: absolute;
}
#virtualKeyboard #copyrights a {
font-size: 9px;
color: blue;
cursor: default;
outline: 0;
}
/**
*
* Styles for the IME field
*
*/
#VirtualKeyboardIME {
background: #fff;
border:1px solid #333;
position: absolute;
width: 200px;
}
#VirtualKeyboardIME div.IMEContent {
border: 1px solid #333;
border-top: 0;
border-bottom: 0;
height: 21px;
line-height: 21px;
margin: 0 12px;
overflow: auto;
padding: 0 3px;
white-space: nowrap;
}
#VirtualKeyboardIME div.left {
border-bottom: 10px solid #fff;
border-left: 0px solid black;
border-right: 10px solid #000;
border-top: 10px solid #fff;
position: absolute;
left: 1px;
font-size: 1px;
overflow: hidden;
}
#VirtualKeyboardIME div.right {
border-bottom: 10px solid #fff;
border-left: 10px solid #000;
border-right: 0px solid black;
border-top: 10px solid #fff;
float: right;
position: absolute;
right: 1px;
overflow: hidden;
}
/************************************
* Place for the locale-dependent styles
* overload fonts here
*
* Language-dependend class name is equal to uppercased language domain code (ZH in zh-CN)
*/
#virtualKeyboard #kbDesk.ZH div.kbButton span {
font-family: MingLiU, SimSun, "Arial Unicode MS";
font-size: 13px;
}
#virtualKeyboard #kbDesk.ZH div.kbButton span.alted,
#virtualKeyboard #kbDesk.ZH div.kbButton span.shifted {
font-size: 9px;
}
#VirtualKeyboardIME.ZH div.IMEContent {
font-family: SimSun, "Arial Unicode MS";
font-size: 16px;
}

View file

@ -0,0 +1,695 @@
/*
* $Id: documentselection.js 413 2008-05-16 21:30:59Z wingedfox $
* $HeadURL: https://svn.debugger.ru/repos/jslibs/BrowserExtensions/tags/BrowserExtensions.018/documentselection.js $
*
* Class implements cross-browser work with text selection
*
* @author Ilya Lebedev
* @author $Author: wingedfox $
* @modified $Date: 2008-05-17 01:30:59 +0400 (Сбт, 17 Май 2008) $
* @version $Rev: 413 $
* @license LGPL
*/
/*
* @class DocumentSelection
*/
DocumentSelection = new function () {
var self = this;
/*
* Stores hash of keys, applied to elements
*
* @type Object
* @scope private
*/
var keys = {
'prevCalcNode' : '__prevCalcNode'
}
//---------------------------------------------------------------------------
// PRIVATES
//---------------------------------------------------------------------------
/**
* Calls specified method with the supplied params
* This is done to process only correct requests
*
* @param {Function} method to call
* @param {Array} arguments of [target, param1, paramN]
* @return {Object} method call result or false, if any error happened
* @scope private
*/
var callMethod = function (m, arg) {
var el = arg[0]
,id
,module = ""
if (!el || !el.tagName) return false;
switch (arg[0].tagName.toLowerCase()) {
case 'input':
if (el.type && el.type != 'text' && el.type != 'password') return false;
case 'textarea':
module = "input";
break;
case 'iframe':
module = "frame";
arg[0] = el.contentWindow;
break;
default:
return false;
}
/*
* instantiate the module
*/
if ('function' == typeof self.module[module])
self.module[module] = new self.module[module](keys);
/*
* throw the exception, is method is not implemented
*/
if (!self.module[module] || !self.module[module][m])
throw new Error ('Method \''+m+'\' is not implemented for DocumentSelection \''+module+'\' module.');
return self.module[module][m].apply(self,arg);
}
/**
* Keeps scrolling on the place for browsers, those don't support this natively
*
* @param {HTMLElement} el target element
* @param {Number} ot old scrollTop property
* @param {Number} ol old scrollLeft property
* @scope private
*/
var keepScroll = function (el,ot,ol) {
if (window.getSelection && 'iframe'!=el.tagName.toLowerCase()) {
var q = self.getSelectionOffset(el)
if (el.contentWindow) el = el.contentWindow.document.body;
if (ot>q.y) el.scrollTop = q.y;
else if (ot+el.clientHeight>q.y) el.scrollTop = ot;
else el.scrollTop = q.y-el.clientHeight/2;
if (ol>q.x) el.scrollLeft = q.x;
else if (ol+el.clientWidth>q.x) el.scrollLeft = ol;
else el.scrollLeft = q.x-el.clientWidth/2;
}
}
//---------------------------------------------------------------------------
// SETTERS
//---------------------------------------------------------------------------
/**
* getSelectionRange wrapper/emulator
*
* @param {HTMLElement}
* @param {Number} start position
* @param {Number} end position
* @param {Boolean} related indicates calculation of range relatively to current start point
* @return void
* @scope public
*/
self.setRange = function(el, start, end, related) {
var ot = el.scrollTop
,ol = el.scrollLeft
/*
* set range on relative coordinates
*/
if (related) {
var st = self.getStart(el);
end = st+end;
start = st+start;
}
if (start < 0) start = 0;
if (end < start) end = start;
callMethod ('setRange',[el,start,end]);
keepScroll(el,ot,ol);
}
//---------------------------------------------------------------------------
// GETTERS
//---------------------------------------------------------------------------
/**
* Return contents of the current selection
*
* @param {HTMLElement} el element to look position on
* @return {String}
* @scope public
*/
self.getSelection = function(el) {
return callMethod('getSelection',[el]);
}
/**
* getSelectionStart wrapper/emulator
* adapted version
*
* @param {HTMLElement} el element to calculate end position for
* @return {Number} start position
* @scope public
*/
self.getStart = function (el) {
return callMethod('getPos',[el,true]);
}
/*
* getSelectionEnd wrapper/emulator
* adapted version
*
* @param {HTMLElement} el element to calculate end position for
* @return {Number} start position
* @scope public
*/
self.getEnd = function (el) {
return callMethod('getPos',[el,false]);
}
/*
* Return cursor position for supplied field
*
* @param {HTMLElement} element to get cursor position from
* @return {Number} position
* @scope public
*/
self.getCursorPosition = function (el) {
return self.getStart(el);
}
//---------------------------------------------------------------------------
// MISC FUNCTIONS
//---------------------------------------------------------------------------
/*
* Insert text at cursor position
*
* @param {HTMLElement} text field to insert text
* @param {String} text to insert
* @scope public
*/
self.insertAtCursor = function (el, val, keep) {
var ot = el.scrollTop
,ol = el.scrollLeft
if (!keep) {
callMethod('del',[el]);
}
var pos = callMethod('ins',[el,val]);
keepScroll(el,ot,ol);
return pos;
}
/*
* Wraps selection with start and end text
*
* @param {HTMLElement} text field to insert text
* @param {String} start text at the beginnging of the selection
* @param {String} end text at the end of the selection
* @scope public
*/
self.wrapSelection = function (el, start, end) {
var s = self.getCursorPosition(el)
,e = self.getEnd(el)
if (s==e) {
self.insertAtCursor(el,start+end);
} else {
self.insertAtCursor(el,start,true);
self.setRange(el,e+start.length,e+start.length);
self.insertAtCursor(el,end,true);
}
}
/*
* Deletes char at cursor position
*
* @param {HTMLElement} text field to delete text
* @param {Boolean} delete text before (backspace) or after (del) cursor
* @scope public
*/
self.deleteAtCursor = function (el, after) {
if (!self.getSelection(el)) {
if (after)
self.setRange(el,0,1,true);
else
self.setRange(el,-1,0,true);
}
return self.deleteSelection(el);
}
/**
* Removes the selection, if available
*
* @param {HTMLElement} el field to delete text from
* @scope public
*/
self.deleteSelection = function (el) {
var ol = el.scrollLeft
,ot = el.scrollTop
,ret = callMethod('del',[el]);
keepScroll(el,ot,ol);
return ret;
}
/**
* Method is used to caclulate pixel offsets for the selection in TextArea (other inputs are not tested yet)
*
* @param {HTMLTextareaElement} el target to calculate offsets
* @return {Object} {x: horizontal offset, y: vertical offset, h: height offset}
* @scope public
*/
self.getSelectionOffset = function (el) {
return callMethod('getSelectionOffset',[el],true);
}
}
DocumentSelection.module = {
/**
* Module processing selection in the 'input' and 'textarea' fields
*
* @param {Object} keys properties, registered for use in DS
* @scope protected
*/
'input' : function (keys) {
var self=this;
/**
* Special document node, used to calculate range offsets in Mozilla
*
* @type HtmlDivElement
* @scope private
*/
var offsetCalculator = null;
/**
* Returns selection start or end position in absolute chars from the field start
*
* @param {HTMLInputElement, HTMLTextareaElement} el input or textarea to get position from
* @param {Boolean} start get start or end selection position
* @return {Number} offset from the beginning
* @scope private
*/
self.getPos = function (el, start) {
var off;
try {
el.setActive();
if (start)
off = Math.abs(el.document.selection.createRange().moveStart("character", -100000000));
else
off = Math.abs(el.document.selection.createRange().moveEnd("character", -100000000));
/*
* test for the TEXTAREA's dumb behavior
*/
if (el.tagName.toLowerCase() != 'input') {
/*
* calculate node offset
*/
var r = el.document.body.createTextRange();
r.moveToElementText(el);
var sTest = Math.abs(r.moveStart("character", -100000000));
off -= sTest;
}
} catch (e) {
try {
off = (start?el.selectionStart:el.selectionEnd);
} catch (e) {
off = 0;
}
}
return off;
}
/**
* Removes the selection, if available
*
* @param {HTMLElement} el field to delete text from
* @return {String} deleted substring
* @scope public
*/
self.del = function (el) {
var ret = ""
,s = self.getPos(el,true)
,e = self.getPos(el,false)
if (s!=e) {
/*
* check for IE, because Opera does use \r\n sequence, but calculate positions correctly
*/
var tmp = document.selection&&!window.opera?el.value.replace(/\r/g,""):el.value;
ret = tmp.substring(s,e);
el.value = tmp.substring(0, s)+tmp.substring(e,tmp.length);
self.setRange(el,s,s);
}
return ret;
}
/**
* Inserts text to the textarea
*
* @param {HTMLElement} text field to insert text
* @param {String} text to insert
* @return {Number} new cursor position
* @scope public
*/
self.ins = function (el,val) {
var ret = ""
,s = self.getPos(el,true)
/*
* check for IE, because Opera does use \r\n sequence, but calculate positions correctly
*/
var tmp = document.selection&&!window.opera?el.value.replace(/\r/g,""):el.value;
el.value = tmp.substring(0,s)+val+tmp.substring(s,tmp.length);
s += val.length;
self.setRange(el,s,s);
return s;
}
/**
* Return contents of the current selection
*
* @param {HTMLElement} el element to look position on
* @param {Number} s start position
* @param {Number} e end position
* @return {String}
* @scope public
*/
self.getSelection = function (el) {
var s = self.getPos(el,true),
e = self.getPos(el,false)
/*
* w/o this check content might be duplicated on delete
*/
if (e<s) e = s;
/*
* check for IE, because Opera does use \r\n sequence, but calculate positions correctly
*/
var tmp = document.selection&&!window.opera?el.value.replace(/\r/g,""):el.value;
return tmp.substring(s,e);
}
/**
* Sets the selection range
*
* @param {HTMLElement}
* @param {Number} start position
* @param {Number} end position
* @return void
* @scope public
*/
self.setRange = function (el,start,end) {
if ('function' == typeof el.setSelectionRange) {
/*
* for Mozilla
*/
try {el.setSelectionRange(start, end)} catch (e) {}
} else {
/*
* for IE
*/
var range;
/*
* just try to create a range....
*/
try {
range = el.createTextRange();
} catch(e) {
try {
range = el.document.body.createTextRange();
range.moveToElementText(el);
} catch(e) {
return false;
}
}
el.focus();
range.collapse(true);
range.moveStart("character", start);
range.moveEnd("character", end - start);
range.select();
}
}
/**
* Method is used to caclulate pixel offsets for the selection in TextArea (other inputs are not tested yet)
*
* @param {HTMLTextareaElement} el target to calculate offsets
* @return {Object} {x: horizontal offset, y: vertical offset, h: height offset}
* @scope public
*/
self.getSelectionOffset = function (el) {
var range;
if ('function' == typeof el.setSelectionRange) {
/*
* For Mozilla
*/
if (!offsetCalculator) {
/*
* create hidden div, which will 'emulate' the textarea
* it's put 'below the ground', because toggling block/none is too expensive
*/
offsetCalculator = document.createElement('td');
document.body.appendChild(offsetCalculator);
}
/*
* store the reference to last-checked object, to prevent recalculation of styles
*/
if (offsetCalculator[keys.prevCalcNode] != el) {
offsetCalculator[keys.prevCalcNode] = el;
var cs = document.defaultView.getComputedStyle(el, null);
for (var i in cs) {
try {if (cs[i]) offsetCalculator.style[i] = cs[i];}catch(e){}
}
offsetCalculator.style.overflow = 'auto';
offsetCalculator.style.position = 'absolute';
offsetCalculator.style.visibility = 'hidden';
offsetCalculator.style.zIndex = '-10';
offsetCalculator.style.left="-10000px";
offsetCalculator.style.top="-10000px";
offsetCalculator.style.backgroundColor = 'yellow';
}
/*
* caclulate offsets to target and move div right below it
*/
var range = document.createRange()
,val = el.value || " ";
if ('input'==el.tagName.toLowerCase()) {
offsetCalculator.style.width = 'auto'
offsetCalculator.style.whiteSpace = 'nowrap';
} else {
offsetCalculator.style.whiteSpace = 'off'==el.getAttribute('wrap')?"pre":"";
}
val = val.replace(/\x20\x20/g,"\x20\xa0").replace(/</g,"&lt;").replace(/>/g,"&gt");
offsetCalculator.innerHTML = ( val.substring(0,el.selectionStart-1)+"<span>"+val.substring(el.selectionStart-1,el.selectionStart)+"</span>"
+val.substring(el.selectionStart)).replace(/\n/g,"<br />")
.replace(/\t/g,"<em style=\"white-space:pre\">\t</em>")
/*
* span is used to find the offsets
*/
var span = offsetCalculator.getElementsByTagName('span')[0];
span.style.border = '1px solid red';
range.offsetLeft = span.offsetLeft// - el.scrollLeft + span.clientWidth;
range.offsetTop = span.offsetTop// - el.scrollTop;
range.offsetHeight = span.offsetHeight;
if ("\n"==val.charAt(el.selectionStart-1)) range.offsetTop += range.offsetHeight*2;
span = null;
} else if (document.selection && document.selection.createRange) {
/*
* For IE
*/
range = document.selection.createRange();
/*
* IE does not allow to calculate lineHeight, but this check is easy
*/
range.offsetHeight = Math.round(range.boundingHeight/(range.text.replace(/[^\n]/g,"").length+1));
if (el.tagName && 'textarea'==el.tagName.toLowerCase()) {
var xy = DOM.getOffset(el)
range = {
'offsetTop' : range.offsetTop-xy.y+DOM.getBodyScrollTop()
,'offsetLeft' : range.offsetLeft-xy.x+DOM.getBodyScrollLeft()
,'offsetHeight' : range.offsetHeight
}
}
}
if (range) {
return {'x': range.offsetLeft, 'y': range.offsetTop, 'h': range.offsetHeight};
}
return {'x': 0, 'y': 0, 'h': 0};
}
}
,'frame' : function () {
var self=this;
/**
* Returns selection start or end position in absolute chars from the field start
*
* @param {HTMLInputElement, HTMLTextareaElement} el input or textarea to get position from
* @param {Boolean} start get start or end selection position
* @return {Number} offset from the beginning
* @scope private
*/
self.getPos = function (el, start) {
var pos = 0
if ('function' == typeof el.getSelection) {
/*
* we need to calculate both start and end points, because range could be reversed
* but we can't move selection end point before start one
*/
var sel = el.getSelection()
,sn = sel.anchorNode
,so = sel.anchorOffset
,en = sel.focusNode
,eo = sel.focusOffset
,ss = false
,es = false
,sc = 0
,ec = 0
,cn
,tw=document.createTreeWalker(el.document.body,NodeFilter.SHOW_TEXT,null,false)
while (sn && sn.nodeType != 3) {
sn = sn.childNodes[so]
so = 0;
}
while (en && en.nodeType != 3) {
en = en.childNodes[eo]
eo = 0;
}
while (cn=tw.nextNode()) {
if (cn == en) {
ec += eo
es = true
}
if (cn == sn) {
sc += so
ss = true
}
if (!es) ec += cn.nodeValue.length
if (!ss) sc += cn.nodeValue.length
if (es && ss) break;
}
pos = start?Math.min(ec,sc):Math.max(ec,sc)
} else {
el.document.body.setActive();
pos = Math.abs(el.document.selection.createRange()[start?"moveStart":"moveEnd"]("character", -100000000));
}
return pos;
}
/**
* Removes the selection, if available
*
* @param {HTMLElement} el field to delete text from
* @return {String} deleted substring
* @scope public
*/
self.del = function (el) {
if ('function' == typeof el.getSelection) {
var s = el.getSelection()
,i = s.rangeCount
while (--i>-1) s.getRangeAt(i).deleteContents();
} else if (el.document && el.document.selection) {
el.document.selection.createRange().text = "";
el.document.selection.createRange().select();
}
}
/**
* Inserts text to the textarea
*
* @param {HTMLElement} text field to insert text
* @param {String} text to insert
* @scope public
*/
self.ins = function (el,val) {
var p = self.getPos(el,true)+val.length;
if ('function' == typeof el.getSelection) {
var n = el.document.createTextNode(val)
,s = el.getSelection()
s.getRangeAt(0).insertNode(n);
n.parentNode.normalize();
} else if (el.document && el.document.selection) {
el.document.body.setActive();
el.document.selection.createRange().text = val;
}
self.setRange(el,p,p)
return p;
}
/**
* Return contents of the current selection
*
* @param {HTMLElement} el element to look position on
* @param {Number} s start position
* @param {Number} e end position
* @return {String}
* @scope public
*/
self.getSelection = function (el,s,e) {
if ('function' == typeof el.getSelection) {
var s = el.getSelection();
return s?s.toString():"";
} else if (el.document && el.document.selection) {
return el.document.selection.createRange().text;
}
}
/**
* Sets the selection range
*
* @param {HTMLElement}
* @param {Number} start position
* @param {Number} end position
* @return void
* @scope public
*/
self.setRange = function (el,start,end) {
if ('function' == typeof el.getSelection) {
var sel = el.getSelection();
sel.removeAllRanges();
var r = el.document.createRange()
,cnt = 0
,cl = 0
,cn
,pn
,tw=document.createTreeWalker(el.document.body,NodeFilter.SHOW_TEXT,null,false);
while ((cn=tw.nextNode())&&(!cn.nodeValue.length||(cnt+cn.nodeValue.length < start))) {
pn = cn;
cnt += cn.nodeValue.length;
}
/*
* explicitly set range borders
*/
if (cn||(cn=pn)) {
r.setStart(cn,start-cnt);
r.setEnd(cn,start-cnt);
}
if (cn) {
do {
if (cn.nodeType != 3) continue;
if (cnt+cn.nodeValue.length < end) {
cnt += cn.nodeValue.length;
} else {
r.setEnd(cn,end-cnt);
break;
}
} while (cn=tw.nextNode())
}
sel.addRange(r);
} else if (el.document && el.document.selection) {
el.document.body.setActive();
var r = el.document.selection.createRange()
r.moveToElementText(el.document.body);
r.move("character",start);
r.moveEnd("character",end-start);
r.select();
}
}
/**
* Method is used to calculate pixel offsets for the selection in TextArea (other inputs are not tested yet)
*
* @param {HTMLTextareaElement} el target to calculate offsets
* @return {Object} {x: horizontal offset, y: vertical offset, h: height offset}
* @scope public
*/
self.getSelectionOffset = function (el) {
var off = {'x':0, 'y':0, 'h':0};
if ('function' == typeof el.getSelection) {
var r = el.getSelection().getRangeAt(0)
,e = r.endOffset
,s = el.document.createElement('span')
,n = s;
s.style.borderLeft='1px solid red';
r.insertNode(s);
off.h = n.offsetHeight;
while (n.offsetParent) {
off.x += n.offsetLeft;
off.y += n.offsetTop;
n = n.offsetParent
}
s.parentNode.removeChild(s);
if (e-r.endOffset) {
r.setEnd(r.endContainer.nextSibling,e-r.endOffset);
el.getSelection().addRange(r)
}
} else if (el.document && el.document.selection) {
var r = el.document.selection.createRange()
off.h = r.boundingHeight
off.x = r.offsetLeft;
off.y = r.offsetTop;
}
return off;
}
}
}

View file

@ -0,0 +1,338 @@
/*
* $Id: dom.js 378 2007-12-13 10:07:23Z wingedfox $
* $HeadURL: https://svn.debugger.ru/repos/jslibs/BrowserExtensions/tags/BrowserExtensions.018/dom.js $
*
* DOM-related stuff and CSS manipulation class
*
* @author Ilya Lebedev
* @author $Author: wingedfox $
* @modified $Date: 2007-12-13 13:07:23 +0300 (Чтв, 13 Дек 2007) $
* @version $Rev: 378 $
* @license LGPL
* @depends helpers.js
* @depends arrayextensions.js
*/
if (isUndefined(DOM)) var DOM = {};
/**
* Performs parent lookup by
* - node object: actually it's "is child of" check
* - tagname: getParent(el, 'li') == getParent(el, 'tagName', 'LI')
* - any node attribute
*
* @param {HTMLElement} el source element
* @param {HTMLElement, String} cp DOMNode or string tagname or string attribute name
* @param {String} vl optional attribute value
* @return {HTMLElement, Null}
* @scope public
*/
DOM.getParent = function (el /* : HTMLElement */, cp /* :String, HTMLElement */, vl /* :String */) /* :HTMLElement */ {
if (el == null) return null;
else if (el.nodeType == 1 &&
((!isUndefined(vl) && el[cp] == vl) ||
('string' == typeof cp && DOM.hasTagName(el, cp)) ||
el == cp)) return el;
else return arguments.callee(el.parentNode, cp, vl);
};
/**
* Calculates the offset for the DOM node from top left corner
*
* @author Matt Kruse
* @see http://javascripttoolbox.com/lib/objectposition/index.php
* @param {HTMLElement} el
* @return {Object} x: horizontal offset, y: vertical offset
* @scope public
*/
DOM.getOffset = function (el /* :HTMLElement */) /* :Object */ {
var fixBrowserQuirks = true
,o = el
,left = 0
,top = 0
,width = 0
,height = 0
,parentNode = null
,offsetParent = null;
if (o==null) return null;
offsetParent = o.offsetParent;
var originalObject = o
,el = o; // "el" will be nodes as we walk up, "o" will be saved for offsetParent references
while (el.parentNode!=null) {
el = el.parentNode;
if (el.offsetParent!==null) {
var considerScroll = true;
/*
In Opera, if parentNode of the first object is scrollable, then offsetLeft/offsetTop already
take its scroll position into account. If elements further up the chain are scrollable, their
scroll offsets still need to be added in. And for some reason, TR nodes have a scrolltop value
which must be ignored.
*/
if (fixBrowserQuirks && window.opera) {
if (el==originalObject.parentNode || el.nodeName=="TR") {
considerScroll = false;
}
}
if (considerScroll) {
if (el.scrollTop && el.scrollTop>0) {
top -= el.scrollTop;
}
if (el.scrollLeft && el.scrollLeft>0) {
left -= el.scrollLeft;
}
}
}
// If this node is also the offsetParent, add on the offsets and reset to the new offsetParent
if (el == offsetParent) {
left += o.offsetLeft;
if (el.clientLeft && el.nodeName!="TABLE") {
left += el.clientLeft;
}
top += o.offsetTop;
if (el.clientTop && el.nodeName!="TABLE") {
top += el.clientTop;
}
o = el;
if (o.offsetParent==null) {
if (o.offsetLeft) {
left += o.offsetLeft;
}
if (o.offsetTop) {
top += o.offsetTop;
}
}
offsetParent = o.offsetParent;
}
}
if (originalObject.offsetWidth) {
width = originalObject.offsetWidth;
}
if (originalObject.offsetHeight) {
height = originalObject.offsetHeight;
}
return {'x':left, 'y':top, 'width':width, 'height':height};
};
//DOM.getOffset = function (el /* :HTMLElement */) /* :Object */ {
/*
var xy = {'x' : el.offsetLeft , 'y' : el.offsetTop};
if (el.offsetParent) {
var xy1 = arguments.callee(el.offsetParent);
xy.x += xy1.x;
xy.y += xy1.y;
}
return xy;
}
*/
/**
* Returns the width of the window canvas
*
* @return {Number}
* @scope public
*/
DOM.getClientWidth = function () /* :Number */{
var w=0;
if (self.innerHeight) w = self.innerWidth;
else if (document.documentElement && document.documentElement.clientWidth) w = document.documentElement.clientWidth;
else if (document.body) w = document.body.clientWidth;
return w;
};
/**
* Returns the height of the window canvas
*
* @return {Number}
* @scope public
*/
DOM.getClientHeight = function () /* :Number */{
var h=0;
if (self.innerHeight) h = self.innerHeight;
else if (document.documentElement && document.documentElement.clientHeight) h = document.documentElement.clientHeight;
else if (document.body) h = document.body.clientHeight;
return h;
};
/**
* Returns the height of the scrolled area for the body
*
* @return {Number}
* @scope public
*/
DOM.getBodyScrollTop = function () /* :Number */{
return self.pageYOffset || (document.documentElement && document.documentElement.scrollTop) || (document.body && document.body.scrollTop);
};
/**
* Returns the height of the scrolled area for the body
*
* @return {Number}
* @scope public
*/
DOM.getBodyScrollLeft = function () /* :Number */{
return self.pageXOffset || (document.documentElement && document.documentElement.scrollLeft) || (document.body && document.body.scrollLeft);
};
/**
* Calculates cursor position properly
*
* @param {Event} e event object to get cursor positions from
* @return {Object} object with x and y cursor positions
* @scope protected
* @see http://hartshorne.ca/2006/01/23/javascript_cursor_position/
* @author Beau Hartshorne
*/
DOM.getCursorPosition = function (e) {
if (e.pageX || e.pageY) return {'x': e.pageX, 'y': e.pageY};
var de = document.documentElement || document.body;
return {'x': e.clientX + de.scrollLeft - (de.clientLeft || 0)
,'y': e.clientY + de.scrollTop - (de.clientTop || 0)};
};
/**
* Checks, if property matches a tagname(s)
*
* @param {HTMLElement} prop
* @param {String, Array} tags
* @return {Boolean}
* @scope public
*/
DOM.hasTagName = function (prop /* :HTMLElement */, tags /* :String, Array */) {
if (isString(tags)) tags = [tags];
if (!isArray(tags) || isEmpty(tags) || isUndefined(prop) || isEmpty(prop.tagName)) return false;
var t = prop.tagName.toLowerCase();
for (var i=0, tL=tags.length; i<tL; i++) {
if (tags[i].toLowerCase() == t) return true;
}
return false;
};
/**
* Return the actual rgb color value from the following formats
* #rrggbb
* #rgb
* rgb (0..255, 0..255,0..255)
* rgb (0..100%, 0..100%,0..100%)
* <color_name>
*
* @param {String} from attr name
* @return {Array} r,g,b values
* @scope public
*/
DOM.color2rgb = function (prop) {
var e;
/*
* note, properties like borderColor might have the series of colors
*/
if (/^([a-z]+)($|\s[a-z]+)/i.test(prop)) {
var d = document.body, ov = d.vLink;
d.vLink = prop.split(" ")[0];
prop = d.vLink;
d.vLink = ov;
}
try {
if (e = prop.match(/^#([\da-f]{6})$/i))
return e=parseInt(e[1],16),[(e&0xff0000)>>16,(e&0xff00)>>8,(e&0xff)]
else if (e = prop.match(/^#([\da-f]{3})$/i)) {
return e=parseInt(e[1],16),[((e&0xf00)>>8)*0x11,((e&0xf0)>>4)*0x11,(e&0xf)*0x11];
} else
return (prop.match(/([\d%]+)/g).splice(0,3).map(function(a){ return /%/.test(a)?(parseInt(a)*2.55).toFixed(0):parseInt(a)}))
} catch(err){
}
}
/**
* DOM.CSS is the CSS processing class, allowing to easy mangle class names
*
* @param {HTMLElement} el element to provide interface for
* @scope public
* @constructor
* @class DOM.CSS
* @exception on invalid parameter
* @depends arrayextensions.js
* @depends helpers.js
*/
DOM.CSS = function (el) {
var self = this
/**
* Adds the class name, unlimited number of arguments is supported
*
* @param {String} class classname to apply to the element
* @return {Object} singleton object to chain operations
* @scope public
*/
self.addClass = function() {
var arg = isArray(arguments[0])?arguments[0]:Array.prototype.slice.call(arguments);
self.removeClass(arg);
el.className = el.className+" "+Array.prototype.join.call(arg," ");
return self;
};
/**
* Removes the class name, unlimited number of arguments is supported
*
* @param {String} class classname to apply to the element
* @return {Object} singleton object to chain operations
* @scope public
*/
self.removeClass = function() {
var arg = Array.prototype.join.call((isArray(arguments[0])?arguments[0]:arguments),"|");
if (!arguments.callee.cache) arguments.callee.cache = {}
var c = arguments.callee.cache
if (!c.hasOwnProperty(arg)) c[arg] = new RegExp("(^|\\s+)("+arg+")(\\s+|$)","g");
el.className = el.className.replace(c[arg]," ");
return self;
};
/**
* Checks classname for the certain class
*
* @param {String} c class name to check for
* @return {Boolean} class name existence
* @scope public
*/
self.hasClass = function(c) {
re=new RegExp("(^|\\s+)"+c+"(\\s+|$)");
return el.className.match(re," "+c+" ");
};
/**
* Returns the actual CSS class for the element
*
* @return {String} css class
* @scope public
*/
self.getClass = function() {
return el.className;
}
/**
* Retrieves class value from class name by pattern
* class-var = "name:value"
* name = [a-z][-a-z0-9]
* value = value | val1:val2:...:valN
*
* @param {String} c class name to check for
* @return {String, Array} value(s)
* @scope public
*/
self.getClassValue = function(c) {
var vals = el.className.match(new RegExp("(^|\\s)"+c+":([^\\s]+)"));
return vals?((vals[2].indexOf(":")+1)?vals[2].split(":")
:vals[2])
:null;
};
/**
* Returns actual style for the element, computed from CSS and inline styles
*
* @param {String} prop optional style property to fetch
* @return {Object} computed style or property value
* @scope public
*/
self.getComputedStyle = function(prop) {
var y;
if (el.currentStyle)
y = prop?el.currentStyle[prop]:el.currentStyle;
else if (window.getComputedStyle) {
y = document.defaultView.getComputedStyle(el,null);
if (prop) y=y[prop];
} else {
y = null;
}
return y;
}
return this;
};

View file

@ -0,0 +1,524 @@
/**
* $Id: eventmanager.js 397 2008-04-22 20:17:57Z wingedfox $
* $HeadURL: https://svn.debugger.ru/repos/jslibs/BrowserExtensions/tags/BrowserExtensions.018/eventmanager.js $
*
* EventManager (EM shorthand) is the class, written to manage event attach/detach/register and so on
*
* @modified $Date: 2008-04-23 00:17:57 +0400 (Срд, 23 Апр 2008) $
* @version $Rev: 397 $
* @license LGPL 2.1 or later
* @author Ilya Lebedev <ilya@lebedev.net>
* @depends helpers.js
*
* @class
* @constructor EventManager
*/
var EM = new function () {
var self = this;
/**
* Events pool
* Element format:
* { 'node' : {HTMLElement},
* ['rootEHCaller' : {Function}]
* 'handler' : {
* <event_name> : [Function[, Function[, ...]]]
* [,<event_name> : [Function[, Function[, ...]]]]
* }
* }
*
* @type Array
* @scope private
*/
var pool = [];
/**
* Unique ID counter, used to attach IDs to the objects
*
* @type Number
* @scope private
*/
var UID = 0;
/**
* List of used keys, applied to different kinds of objects
*
* @type Object
* @scope private
*/
var keys = {
'UEID' : '__eventManagerUniqueElementId'
// ,'UHID' : '__eventManagerUniqueHandlerId'
};
/**************************************************************************
* PROTECTED METHODS
***************************************************************************/
/**
* Method is being binded to any new event handler, then, when called,
* does some unification between browser platforms and calls all binded
* event handlers
*
* @param {Event} e event object
* @scope protected
*/
var rootEventHandler = function (e) {
unifyEvent(e);
var id = null
,hid = null
,el = e.target
,fe = true
,res = true;
if (!e.currentTarget || !(id = e.currentTarget[keys.UEID]) || !(hid = pool[id].handler[e.type])) return;
try {
for (var i=0, hL=hid.length; i<hL; i++) if (isFunction(hid[i])) res=res&&!(false===hid[i].call(e.currentTarget, e));
} catch (err) { setTimeout(function(){throw new Error("Event handler for ["+e.type+"] has failed with exception: \""+err.message+"\"");},10) }
return res;
};
/**
* Performs events cleanup on page unload
* It aims to remove leaking closures
*
* @param {Event} e window.unload event
* @scope protected
*/
var unloadEventHandler = function (e) {
for (var i=pool.length-1,pid=null,el=null; i>=0; i--) {
if (pool[i] && (el=(pid = pool[i]).node)) {
for (var z in pid.handler) {
if (!pid.handler.hasOwnProperty(z)) continue;
try {
if (el.removeEventListener) {
el.removeEventListener(z, pid.rootEHCaller?pid.rootEHCaller:rootEventHandler, false);
} else if (el.detachEvent) {
el.detachEvent('on'+z, pid.rootEHCaller?pid.rootEHCaller:rootEventHandler);
}
} catch (e) {}
pid.handler[z].length = 0;
}
}
el = pid.node = null;
}
if (window.removeEventListener) {
window.removeEventListener(z, arguments.callee, false);
} else {
window.detachEvent('on'+z, arguments.callee);
}
};
/**************************************************************************
* PRIVATE METHODS
***************************************************************************/
/**
* Makes an event clone, it does not dereference objects in the event properties
*
* @param {Event} e event handler
* @return {Object} cloned event
* @scope private
*/
var unifyEvent = function (e) {
var i=self.EU.length
,cur,cur1,k,init
while (i--) {
cur = self.EU[i];
if (cur[0].test(e.type)) {
k=cur[1].length;
init = null;
while (k--) {
cur1 = cur[1][k];
if ('init' == cur1[0]) init = cur1[1]
else if (!e[cur1[0]]) e[cur1[0]] = cur1[1];
}
if (init) init.call(e);
}
}
if (!e.target && e.type != 'unload') e.target = e.srcElement;
return e;
};
/**
* Returns UEID property for the specified element, creates it, if asked
*
* @param {Object} el element to find UEID on
* @param {Boolean} f optional flag to force UEID creation, if not exists
* @retutn {Number} UEID, if > 0
* @scope private
*/
var getUEID = function (el, f) {
return el[keys.UEID] || (f && (el[keys.UEID] = ++UID));
};
/**************************************************************************
* PUBLIC METHODS
***************************************************************************/
/**
* Adds the event listener to the queue
*
* @param {Object} el element to attach event handler to
* @param {String} et event name to attach event handler to (without 'on' prefix)
* @param {Function} h event handler
* @return {Boolean} success state
* @scope public
*/
self.addEventListener = function (el, et, h) {
if (!el || !isFunction(h)) return false;
// if (!el.addEventListener && !el.attachEvent) return false;
/*
* unique identifier is used to keep an eye on the element
*/
var id = getUEID(el, true)
,pid = null
,hid = null;
/*
* prepare pool object, if needed
*/
if (!pool[id]) {
pool[id] = {
'node' : el
,'handler' : {}
}
};
pid = pool[id];
/*
* prepare handlers storage in the pool object, if needed
*/
if (!pid.handler.hasOwnProperty(et)) {
pid.handler[et] = [];
/*
* if we a here, this means that we have not connected to a node yet
* note, we've already made a check for the required methods existense
*/
if (el.addEventListener) {
el.addEventListener(et, rootEventHandler, false);
} else if (el.attachEvent) {
/*
* this workaround is used to avoid IE's lack of currentTarget property
*/
pid.rootEHCaller = function(e) {
e.currentTarget = pid.node;//pool[id].node;
var res = rootEventHandler(e);
e.currentTarget = null;
return res;
};
el.attachEvent('on'+et, pid.rootEHCaller);
}
};
hid = pid.handler[et];
/*
* finally, attach handler, if it was not attached before
*/
if (hid.indexOf(h)==-1) {
hid[hid.length] = h;
return true;
}
return false;
};
/**
* Removes the event listener from the queue
*
* @param {Object} el element to attach event handler to
* @param {String} et event name to attach event handler to (without 'on' prefix)
* @param {Function} h event handler
* @return {Boolean} success state
* @scope public
*/
self.removeEventListener = function (el,et,h) {
if (!el || !isFunction(h)) return false;
var id = getUEID(el)
,pid = pool[id]
,eid = null;
if (pid && (eid = pid.handler[et])) {
/*
* we've found an event handler
*/
eid.splice(eid.indexOf(h),1);
if (0 == eid.length) {
delete pid.handler[et];
/*
* remove the actual listener
*/
if (el.removeEventListener) {
el.removeEventListener(et, pid.rootEHCaller?pid.rootEHCaller:rootEventHandler, false);
} else if (el.detachEvent) {
el.detachEvent('on'+et, pid.rootEHCaller?pid.rootEHCaller:rootEventHandler);
}
}
return true;
}
return false;
};
/**
* Dispatch custom events on the specified element
*
* @param {Object} e event object itself
* @return {Boolean} cancelled status
* @scope public
*/
self.dispatchEvent = function (e) {
var res = rootEventHandler(e);
return res;
};
/**
* Registers new event handler for any object
* It's a good idea to register events on the object instances, really
*
* @param {Object} o object to register new event on
* @param {String} n bindable event name
* @param {Boolean} b optional bubbling allowed flag
* @param {Function} d optional default action function
* @return {EMEvent} object, allowing to invoke events
* @scope public
* @see EM.EventTarget
*/
self.registerEvent = function (o, n, b, d) {
var id = getUEID(o,true);
if (!pool[id]) {
pool[id] = {'node' : o,
'handler' : []};
} else {
pool[id].node = o;
}
return new EM.EventTarget(o, n, b, d);
};
/**
* Performs object initialization
*
*/
var __construct = function() {
/*
* for IE, to dereference event handlers and remove memory leaks
*/
if (window.attachEvent && !window.addEventListener) {
window.attachEvent('onunload',unloadEventHandler);
}
};
__construct();
};
/******************************************************************************
* STATIC METHODS
******************************************************************************/
EM.preventDefaultAction = function(e) {
e.preventDefault();
}
EM.stopPropagationAction = function(e) {
e.stopPropagation();
}
/******************************************************************************
* SUPPLEMENTARY CLASSES
******************************************************************************/
/**
* EventTarget base class
* Used to create self-containing event object
*
* @class EM.EventTarget
* @constructor
* @param {String} name event name
* @param {Null, Object} obj event execution context (this), window if null passed
* @param {Boolean} bubble flag allowing event to bubble across element.parentNode
* @param {Function} def default action for the event
*/
EM.EventTarget = function (obj, name, bubble, def) {
var self = this;
/**
* Indicates possible bubbling, by default bubbling is not allowed
*
* @type Boolean
* @default false
* @scope private
*/
var canBubble = !!bubble;
/**
* Stores function, performing default action for the event
*
* @type Function
* @scope private
*/
var defaultAction = isFunction(def)?def:null;
/**************************************************************************
* PRIVATE METHODS
***************************************************************************/
/**************************************************************************
* PROTECTED METHODS
***************************************************************************/
/**
* Used to trigger created event on the supplied object or on the 'obj' constructor param
*
*
* @param {Object} el optional element to trigger event on (.target property in the event object)
* @param {Object} event data
*/
self.trigger = function (el, data) {
if (!(arguments.length-1) && el!=obj) {
data = el;
el = null;
}
if (!el) el = obj;
var e = {}
,res = true
,undef = true
,tmp = null
for (var i in data) {
if (data.hasOwnProperty(i)) e[i] = data[i];
}
/*
* set defaults
*/
canBubble = !!bubble;
defaultAction = def;
/*
* start the go
*/
do {
e.preventDefault = preventDefault;
e.stopPropagation = stopPropagation;
e.target = el;
e.currentTarget = el;
e.type = name;
tmp = EM.dispatchEvent(e);
undef &= (isUndefined(tmp))
res &= !(false===tmp);
} while ((el = el.parentNode) && canBubble);
/*
* try to execute the default action
*/
if (isFunction(defaultAction) && res && !undef) {
defaultAction(e);
}
return (defaultAction && res && !undef);
};
/**
* Prevents default event action
*
* @scope protected
*/
var preventDefault = function () {
defaultAction = null;
};
/**
* Stops bubbling
*
* @scope protected
*/
var stopPropagation = function () {
canBubble = false;
};
};
/**
* Namespace for event unification routines
*
* @type Array
* @scope protected
*/
EM.EU = [
[/./ , [
/**
* Prevents event from calling default event handler
*
* @scope protected
*/
['preventDefault', function() {
this.returnValue = false;
}
]
/**
* Prevents event from futher bubbling
*
* @scope protected
*/
,['stopPropagation', function() {
this.cancelBubble = true;
}
]
]]
,[/^mouse(over|out|down|up)/ , [
/**
* Used to detect left or right button pressed.
* Due to some browser inconsistense, middle button is ignored
*
* @return {Number} 1 - left button, 2 - right button
* @scope protected
*/
['getButton', function () {
return this.button==2?2:1
}
]
,['EM_MB_LEFT', '1']
,['EM_MB_RIGHT', '2']
]]
,[/^key(down|up|press)/ , [
/**
* Used to return browser-independend keycode
*
* @return {Number} fixed key code
* @scope protected
*/
['getKeyCode', function () {
switch (this.keyCode) {
case 189: return 109;
case 187: return 61;
case 186: return 59;
default: return this.keyCode;
}
}
]
/**
* Calculates 'repeat' property for the key events
*
* @return {Number} 0 means no repeating keystroke detected
* @scope protected
*/
,['getRepeat', function () {
return arguments.callee.repeat
}
]
,['init', function () {
var ac = this.getRepeat
if ('keyup' == this.type) {
ac.repeat = 0;
ac.keyCode = 0;
} else if ('keydown' == this.type) {
ac.repeat = ac.keyCode==this.keyCode;
ac.keyCode=this.keyCode;
}
}
]
]]
];
/*
* register core event handler, domload
* it's called right on the document initialization, before images complete load
*/
(function (){
var evt = EM.registerEvent(window,'domload')
,executed = false
,clearEvents = function() {
//For IE
EM.removeEventListener(document, 'propertychange', handlers.ie);
//For Mozilla
EM.removeEventListener(document, 'DOMContentLoaded', handlers.mz);
//For someone else
EM.removeEventListener(window, 'load', handlers.mz);
}
,handlers = { 'ie' : function(e) {
if (window.event.propertyName == 'activeElement' && !executed) {
evt.trigger(window);
clearEvents();
executed = true;
}
}
,'mz' : function (e) {if(!executed)evt.trigger(window); executed=true;}
};
//For IE
EM.addEventListener(document, 'propertychange', handlers.ie);
//For Mozilla
EM.addEventListener(document,'DOMContentLoaded', handlers.mz);
//For Safari and Opera
if(/WebKit|Khtml/i.test(navigator.userAgent)||(window.opera&&parseInt(window.opera.version())<9))(function(){/loaded|complete/.test(document.readyState)?(evt.trigger(window),executed=true):setTimeout(arguments.callee,100)})();
//For someone else
EM.addEventListener(window, 'load', handlers.mz);
})();

View file

@ -0,0 +1,325 @@
// Array Extensions v1.0.7
// documentation: http://www.dithered.com/javascript/array/index.html
// license: http://creativecommons.org/licenses/by/1.0/
// code by Chris Nott (chris[at]dithered[dot]com)
// code by Ilya Lebedev (ilya[at]lebedev[dot]net)
// Array.concat() - Join two arrays
if (isUndefined(Array.prototype.concat)) {
Array.prototype.concat = function (secondArray) {
var firstArray = this.copy();
for (var i = 0, saL = secondArray.length; i < saL; i++) {
firstArray[firstArray.length] = secondArray[i];
}
return firstArray;
};
}
// Array.copy() - Copy an array
if (isUndefined(Array.prototype.copy)) {
Array.prototype.copy = function() {
var copy = new Array();
for (var i = 0, tL = this.length; i < tL; i++) {
copy[i] = this[i];
}
return copy;
};
}
// Array.pop() - Remove the last element of an array and return it
if (isUndefined(Array.prototype.pop)) {
Array.prototype.pop = function() {
var lastItem = undefined;
if ( this.length > 0 ) {
lastItem = this[this.length - 1];
this.length--;
}
return lastItem;
};
}
// Array.push() - Add an element to the end of an array
if (isUndefined(Array.prototype.push)) {
Array.prototype.push = function() {
var currentLength = this.length;
for (var i = 0; i < arguments.length; i++) {
this[currentLength + i] = arguments[i];
}
return this.length;
};
}
// Array.shift() - Remove the first element of an array and return it
if (isUndefined(Array.prototype.shift)) {
Array.prototype.shift = function() {
var firstItem = this[0];
for (var i = 0, tL = this.length - 1; i < tL; i++) {
this[i] = this[i + 1];
}
this.length--;
return firstItem;
};
}
// Array.slice() - Copy several elements of an array and return them
if (isUndefined(Array.prototype.slice)) {
Array.prototype.slice = function(start, end) {
var temp;
if (end == null || end == '') end = this.length;
// negative arguments measure from the end of the array
else if (end < 0) end = this.length + end;
if (start < 0) start = this.length + start;
// swap limits if they are backwards
if (end < start) {
temp = end;
end = start;
start = temp;
}
// copy elements from array to a new array and return the new array
var newArray = new Array();
for (var i = 0; i < end - start; i++) {
newArray[i] = this[start + i];
}
return newArray;
};
}
// Array.splice() - Splice out and / or replace several elements of an array and return any deleted elements
if (isUndefined(Array.prototype.splice)) {
Array.prototype.splice = function(start, deleteCount) {
if (deleteCount == null || deleteCount == '') deleteCount = this.length - start;
// create a temporary copy of the array
var tempArray = this.copy();
// Copy new elements into array (over-writing old entries)
for (var i = start, aL = start + arguments.length - 2; i < aL; i++) {
this[i] = arguments[i - start + 2];
}
// Copy old entries after the end of the splice back into array and return
var dC = deleteCount - arguments.length + 2;
for (var i = start + arguments.length - 2, tL = this.length - deleteCount + arguments.length - 2; i < tL; i++) {
this[i] = tempArray[i + dC];
}
this.length = this.length - dC;
return tempArray.slice(start, start + deleteCount);
};
}
// Array.unshift - Add an element to the beginning of an array
if (isUndefined(Array.prototype.unshift)) {
Array.prototype.unshift = function(the_item) {
for (var loop = this.length-1 ; loop >= 0; loop--) {
this[loop+1] = this[loop];
}
this[0] = the_item;
return this.length;
};
}
// Array.indexOf - return index of found element or -1 (similar to String.indexOf)
// Don't do the check on 'undefined' because Mozilla does calculate index weirdly
Array.prototype.indexOf = function(needle,begin) {
for (var i=(null==begin||isNaN(begin)||begin<0)?0:Math.round(begin),len = this.length, idx = -1; idx==-1 & i<len; i++) {
idx = (this[i]==needle)?i:idx;
}
return idx;
};
// Array.lastIndexOf - return index of found element or -1 (similar to String.lastIndexOf)
// Don't do the check on 'undefined' because Mozilla does calculate index weirdly
Array.prototype.lastIndexOf = function(needle,end) {
for (var i=(null==end||isNaN(end)||end>this.length)?this.length-1:Math.round(end), idx = -1; idx==-1 & i>-1; i--) {
idx = (this[i]==needle)?i:idx;
}
return idx;
};
// Array.map - maps a function on the array elements
if (isUndefined(Array.prototype.map)) {
Array.prototype.map = function(func) {
if ('function' != typeof func) return this;
var tmp = [];
for (var loop = this.length-1 ; loop >= 0; loop--) {
tmp[loop] = func(this[loop]);
}
return tmp;
};
}
if (isUndefined(Array.prototype.unique)) {
/**
* Method removes dumplicate entries
*
* @return {Array}
* @scope public
*/
Array.prototype.unique = function() /* :Array */{
var tmp = [];
for(var i=0, tL=this.length; i<tL; i++ ) {
if( tmp.indexOf(this[i]) < 0 ) tmp[tmp.length] = this[i];
}
return tmp;
};
}
if (isUndefined(Array.prototype.flatten)) {
/**
* Method flattens 2-dimensional array, when no cols supplied only the cols number from 0th row will be counted
*
* @param {Number, Array} cols columns to insert to resulting array
* @return {Array}
* @scope public
*/
Array.prototype.flatten = function(cols /* :Array */, cd) /* :Array */{
if (this.length<1) return [];
if (isNumeric(cols)) cols = [cols];
var idx = false;
if (isArray(cols)) {
idx = {};
for (var i=0,cL=cols.length;i<cL;i++) idx[cols[i]]=true;
}
var tmp = [];
for (var i=0, tL=this.length; i<tL; i++ ) {
if (isUndefined(this[i])) continue;
if (!isArray(this[i])) {
if (false===idx) tmp[tmp.length] = this[i];
} else {
for (var k=0, cL=this[i].length; k<cL; k++) {
if (false===idx || idx.hasOwnProperty(k)) tmp[tmp.length] = this[i][k];
}
}
}
return tmp;
};
}
if (isUndefined(Array.prototype.filter)) {
/**
* Method returns array with non-empty (not evaluating to false) entries
*
* @param {Function} cbk optional callback function to perform a filter
* @return {Array}
* @scope public
*/
Array.prototype.filter = function(cbk /* :Function */) /* :Array */ {
if (!isFunction(cbk)) cbk = null;
for (var i=0, tL = this.length, tmp = [], ti=null; i<tL;i++) {
ti = cbk?cbk(this[i]):this[i];
if (!isEmpty(ti)) tmp[tmp.length] = ti;
}
return tmp;
};
}
if (isUndefined(Array.prototype.binSearch)) {
/**
* Provides binary search for the sorted array, causes unexpected results on unsorted one
*
* @param {Variant} el element to search for
* @return {Number} array index
* @scope public
*/
Array.prototype.binSearch = function (el,key) {
var l = 0
,r = this.length
,len = Math.max(r-1,0)
,c = Math.ceil(r/2)
,cnt = 0
if (null != key)
while ((!this[c] || el!=this[c][key]) && r>=l) {
if (this[c] && el>this[c][key])
l=c+1
else
r=c-1
c=Math.max(0,Math.ceil((r+l)/2))
}
else
while (el!=this[c] && r>=l) {
if (el>this[c])
l=c+1
else
r=c-1
c=Math.max(0,Math.ceil((r+l)/2));
}
return c
}
}
/**
* heap sort ( N log N )
*
* @scope public
*/
Array.prototype.heapSort = function () {
// prepare the array with special sorting method
if (!this.sift) {
/**
* Innersorting method for the heap sort
*
* @param {Number} low
* @param {Number} up
*/
this.sift = function (low, up) {
var c, tmp = this[low];
while(true){
c = (low << 1) + 1;
if( c > up ) break;
if( c < up && this[c+1][0] > this[c][0] ) c++;
if( tmp[0] >= this[c][0] ) break;
this[low] = this[c]; // 1/2 (mini) swap ))
low = c;
}
this[low] = tmp;
}
}
var tmp,
maximal = this.length - 1,
i = maximal << 1;
while( i >= 0 ){ this.sift(i--, maximal) };
i = maximal;
while( i > 0 ){
// full swap
tmp = this[0];
this[0] = this[i];
this[i] = tmp;
this.sift(0,--i);
}
}
//-----------------------------------------------------------------------------
// STATIC METHODS
//-----------------------------------------------------------------------------
if (isUndefined(Array.range)) {
/**
* Method creates the array with values in the specified range
* 1 argument: create array from min(0, end) to max(0, end) with increment 1
* 2 arguments: create array from min(start, end) to max(start, end) with increment 1
* 3 arguments: create array from min(start, end) to max(start, end) with increment inc
*
* @param {Number} end end position
* @param {Number} start start position
* @param {Number} inc increment
* @return {Array}
* @scope public
*/
Array.range = function(end /* :Number */, start /* :Number */, inc /* :Number */) /* :Array */{
if (!isNumber(end)) return null;
if (!isNumber(inc)) inc = 1;
if (!isNumber(start)) start = 0;
var tmp = []
,mn = Math.min(start, end)
,mx = Math.max(start, end)
,i = Math.abs(inc)
,cnt = -1;
do {
cnt++;
tmp[cnt] = mn;
mn += i;
} while (mn<=mx);
return inc>0?tmp:tmp.reverse();
};
}

View file

@ -0,0 +1,12 @@
/*
* Checks if property is derived from prototype, applies method if it is not exists
*
* @param string property name
* @return bool true if prototyped
* @access public
*/
if ('undefined' == typeof Object.hasOwnProperty) {
Object.prototype.hasOwnProperty = function (prop) {
return !('undefined' == typeof this[prop] || this.constructor && this.constructor.prototype[prop] && this[prop] === this.constructor.prototype[prop]);
}
}

View file

@ -0,0 +1,33 @@
/**************************************************
*
* Extensions for the RegExp object
*
* @author Ilya Lebedev <ilya@lebedev.net>
* @modified $Date: 2007-10-26 19:25:39 +0400 (Птн, 26 Окт 2007) $
* @version $Rev: 339 $
* @license LGPL 2.1 or later
**************************************************/
/**
* Does escape of special regexp characters
*
* Modified version from Simon Willison
*
* @see http://simon.incutio.com/archive/2006/01/20/escape
* @param {String, Array} text to escape
* @return {String} escaped result
* @scope public
*/
RegExp.escape = function(text /* :String, Array */) /* :String */ {
if (!arguments.callee.sRE) {
var specials = [
'/', '.', '*', '+', '?', '|',
'(', ')', '[', ']', '{', '}', '$', '^', '\\'
];
arguments.callee.sRE = new RegExp(
'(\\' + specials.join('|\\') + ')', 'g'
);
}
return isString(text)?text.replace(arguments.callee.sRE, '\\$1')
:(isArray(text)?text.map(RegExp.escape).join("|")
:"");
}

View file

@ -0,0 +1,307 @@
/**
* $Id: string.js 370 2007-11-25 01:39:30Z wingedfox $
* $HeadURL: https://svn.debugger.ru/repos/jslibs/BrowserExtensions/tags/BrowserExtensions.018/ext/string.js $
*
* @author Ildar Shaimordanov
* @author Ilya Lebedev
* @license LGPL
* @version $Rev: 370 $
*/
/**
* Decodes html entities
*
* @return {String} string with decoded entities
* @scope public
*/
String.prototype.entityDecode = function() {
if (!arguments.callee.span) arguments.callee.span = document.createElement('span');
var s = arguments.callee.span;
s.innerHTML = this;
return s.firstChild?s.firstChild.nodeValue:"";
}
/**
* Method is used to trim specified chars from the left of the string
*
* @param {String, Array} c char or char list to be trimmed, default is \s
* @return {String}
* @scope public
*/
String.prototype.ltrim = function(c) {
if (isString(c)) c=c.split("");
if (isArray(c) || isUndefined(c)) {
c = isEmpty(c)?"\\s":RegExp.escape(c);
c = new RegExp("^(?:"+c+")+", "g");
return this.replace(c, "");
}
return this;
}
/**
* Method is used to trim specified list from the right of the string
*
* @param {String, Array} c char or char sequence to be trimmed, default is \s
* @return {String}
* @scope public
*/
String.prototype.rtrim = function(c) {
if (isString(c)) c=c.split("");
if (isArray(c) || isUndefined(c)) {
c = isEmpty(c)?"\\s":RegExp.escape(c);
c = new RegExp("(?:"+c+")+$", "g");
return this.replace(c, "");
}
return this;
}
/**
* Method is used to trim specified chars from the left and the right of the string
*
* @param {String, Array} c char or char list to be trimmed, default is \s
* @return {String}
* @scope public
*/
String.prototype.trim = function(c) {
if (isString(c)) c=c.split("");
if (isArray(c) || isUndefined(c)) {
c = isEmpty(c)?"\\s":RegExp.escape(c);
c = new RegExp("^(?:"+c+")+|(?:"+c+")+$", "g");
return this.replace(c, "");
}
return this;
}
/**
* Duplicates the string
*
* @return {String}
* @scope public
*/
String.prototype.dup = function() {
var val = this.valueOf();
return [val,val].join("");
}
/**
* Repeats string specified number of times
*
* @param {Number} n number of times to repeat the string
* @return {String}
* @scope public
*/
String.prototype.repeat = function(n /* :Number */) /* :String */ {
if (isNaN(n=parseInt(n)) || n<0) return "";
return Array(n+1).join(this.valueOf());
}
/**
* Pads the string to the specified length
*
* @param {Number} n number of times to repeat c
* positive - on the right side
* negative - on the left side
* @param {String} c fill char, space is default
* @return {String}
* @scope public
*/
String.prototype.padding = function(n, c) {
var val = this.valueOf();
n = parseInt(n);
if (!n) return val;
if (isUndefined(c)) c = " ";
var pad = String(c).charAt(0).repeat(Math.abs(n) - this.length);
return (n < 0) ? pad + val : val + pad;
}
/**
* Pads the string on the right side
*
* @param {Number} n number of times to repeat c
* @param {String} c fill char
* @return {String}
* @scope public
*/
String.prototype.padLeft = function(n, c) {
return this.padding(-Math.abs(n), c);
}
/**
* Pads the string on the left side
*
* @param {Number} n number of times to repeat c
* @param {String} c fill char
* @return {String}
* @scope public
*/
String.prototype.padRight = function(n, c) {
return this.padding(Math.abs(n), c);
}
/**
* sprintf(format, argument_list)
*
* The string format function like the one in C/C++, PHP, Perl
* Each conversion specification is defined as below:
*
* %[index][alignment][padding][width][precision]type
*
* index An optional index specifier that changes the order of the
* arguments in the list to be displayed.
* alignment An optional alignment specifier that says if the result should be
* left-justified or right-justified. The default is
* right-justified; a "-" character here will make it left-justified.
* padding An optional padding specifier that says what character will be
* used for padding the results to the right string size. This may
* be a space character or a "0" (zero character). The default is to
* pad with spaces. An alternate padding character can be specified
* by prefixing it with a single quote ('). See the examples below.
* width An optional number, a width specifier that says how many
* characters (minimum) this conversion should result in.
* precision An optional precision specifier that says how many decimal digits
* should be displayed for floating-point numbers. This option has
* no effect for other types than float.
* type A type specifier that says what type the argument data should be
* treated as. Possible types:
*
* % - a literal percent character. No argument is required.
* b - the argument is treated as an integer, and presented as a binary number.
* c - the argument is treated as an integer, and presented as the character
* with that ASCII value.
* d - the argument is treated as an integer, and presented as a decimal number.
* u - the same as "d".
* f - the argument is treated as a float, and presented as a floating-point.
* o - the argument is treated as an integer, and presented as an octal number.
* s - the argument is treated as and presented as a string.
* x - the argument is treated as an integer and presented as a hexadecimal
* number (with lowercase letters).
* X - the argument is treated as an integer and presented as a hexadecimal
* number (with uppercase letters).
*
* @return {String}
* @scope public
*/
String.prototype.sprintf = function() {
var args = isArray(arguments[0])?arguments[0]:arguments
,index = 0
,frmt = this.replace(/%%/g, "\0\0")
,re = /%((?:\d+\$)?)((?:[-0+# ])?)((?:\d+|\*(?:\d+\$)?)?)((?:.(?:\d+|\*(?:\d+\$)?))?)([bcdeEfosuxX])/g;
/*
* The re.exec() method returns the array with the following properties
* wich are used in this function
* x.index contains the substring position found at the origin string
* x[0] contains the found substring
* x[1] contains the explicit parameter number
* x[2] contains the flags
* x[3] contains the minwidth
* x[4] contains the precision
* x[5] contains the type specifier (as [bcdfosuxX])
*/
frmt = frmt.replace(re, function() {
var x = arguments
,sign = false
,ins;
/*
* calculate min width
*/
if (!isUndefined(x[3]) && x[3].indexOf("*")==0) {
x[3] = parseInt(x[3].replace(/\D/g,""))
if (isNaN(x[3])) {
x[3] = args[index];
/*
* increment
*/
index++;
} else {
x[3] = args[x[3]]
}
}
/*
* calculate precision
*/
if ("" != x[4]) {
if (x[4].indexOf("*")==1) {
x[4] = parseInt(x[4].replace(/\D/g,""))
if (isNaN(x[4])) {
x[4] = args[index];
/*
* increment
*/
index++;
} else {
x[4] = args[x[4]]
}
} else {
x[4] = x[4].replace(/\D/,"")
}
x[4] = Math.abs(x[4]);
}
/*
* calculate insertion value
*/
x[1] = parseInt(x[1]);
var ins;
if (isNumeric(x[1])) {
ins = args[x[1]];
} else {
ins = args[index];
/*
* index should be incremented only when no explicit parameter number is specified
*/
index++;
}
switch (x[5]) {
case "b":
if (ins<0) ins = 0x10000000000000000+parseInt(ins);
ins = Number(ins).bin(x[4]);
if (x[4]) ins = ins.substr(0,x[4]);
if (x[2]=='#') ins = '0b'+ins;
break;
case "c":
ins = String.fromCharCode(ins);
break;
case "u":
ins = Math.abs(ins);
case "d":
ins = Math.round(ins);
if (ins<0) {
ins = "-"+Math.abs(ins).dec(x[4]);
} else {
ins = Number(ins).dec(x[4]);
sign = (x[2] == ' ' || x[2] == '+');
}
break;
case "e":
case "E":
if (ins>0) {
sign = (x[2] == ' ' || x[2] == '+');
}
ins = Number(ins).toExponential(x[4]?x[4]:6);
if (x[5]=='E') ins=ins.toUpperCase();
break;
case "f":
if (ins>0) {
sign = (x[2] == ' ' || x[2] == '+');
}
ins = Number(ins).toFixed(isNumeric(x[4])?x[4]:6);
break;
case "o":
if (ins<0) ins = 0x10000000000000000+parseInt(ins);
ins = Number(ins).toString(8);
if (x[4]) ins = ins.substr(0,x[4]);
if (x[2]=='#' && ins != 0) ins = '0'+ins;
break;
case "s":
ins = String(ins);
if (x[4]) ins = ins.substr(0,x[4]);
break;
case "x":
case "X":
if (ins<0) ins = 0x10000000000000000+parseInt(ins);
ins = Number(ins).hex(-x[4]);
if (x[4]) ins = ins.substr(0,x[4]);
if (x[2]=='#') ins = '0x'+ins;
if (x[5]=='X') ins = ins.toUpperCase();
break;
}
if (sign) ins = x[2]+ins;
if (x[3]) ins = (x[2]=='-' || x[3]<0)?ins.padRight(x[3]):ins.padLeft(x[3],x[2]=='0'?0:" ");
return ins;
})
return frmt.replace(/\0\0/g, "%");
}

View file

@ -0,0 +1,388 @@
/**
* $Id: helpers.js 366 2007-11-24 02:27:32Z wingedfox $
* $HeadURL: https://svn.debugger.ru/repos/jslibs/BrowserExtensions/tags/BrowserExtensions.018/helpers.js $
*
* File contains differrent helper functions
*
* @author Ilya Lebedev <ilya@lebedev.net>
* @license LGPL
* @version $Rev: 366 $
*/
//-----------------------------------------------------------------------------
// Variable/property checks
//-----------------------------------------------------------------------------
/**
* Checks if property is undefined
*
* @param {Object} prop value to check
* @return {Boolean} true if matched
* @scope public
*/
function isUndefined (prop /* :Object */) /* :Boolean */ {
return (typeof prop == 'undefined');
}
/**
* Checks if property is function
*
* @param {Object} prop value to check
* @return {Boolean} true if matched
* @scope public
*/
function isFunction (prop /* :Object */) /* :Boolean */ {
return (typeof prop == 'function');
}
/**
* Checks if property is string
*
* @param {Object} prop value to check
* @return {Boolean} true if matched
* @scope public
*/
function isString (prop /* :Object */) /* :Boolean */ {
return (typeof prop == 'string');
}
/**
* Checks if property is number
*
* @param {Object} prop value to check
* @return {Boolean} true if matched
* @scope public
*/
function isNumber (prop /* :Object */) /* :Boolean */ {
return (typeof prop == 'number');
}
/**
* Checks if property is the calculable number
*
* @param {Object} prop value to check
* @return {Boolean} true if matched
* @scope public
*/
function isNumeric (prop /* :Object */) /* :Boolean */ {
return (isNumber(prop)||isString(prop))&&!isNaN(parseInt(prop))&&isFinite(parseInt(prop));
}
/**
* Checks if property is array
*
* @param {Object} prop value to check
* @return {Boolean} true if matched
* @scope public
*/
function isArray (prop /* :Object */) /* :Boolean */ {
return (prop instanceof Array);
}
/**
* Checks if property is regexp
*
* @param {Object} prop value to check
* @return {Boolean} true if matched
* @scope public
*/
function isRegExp (prop /* :Object */) /* :Boolean */ {
return (prop instanceof RegExp);
}
/**
* Checks if property is a boolean value
*
* @param {Object} prop value to check
* @return {Boolean} true if matched
* @scope public
*/
function isBoolean (prop /* :Object */) /* :Boolean */ {
return ('boolean' == typeof prop);
}
/**
* Checks if property is a scalar value (value that could be used as the hash key)
*
* @param {Object} prop value to check
* @return {Boolean} true if matched
* @scope public
*/
function isScalar (prop /* :Object */) /* :Boolean */ {
return isNumeric(prop)||isString(prop)||isBoolean(prop);
}
/**
* Checks if property is empty
*
* @param {Object} prop value to check
* @return {Boolean} true if matched
* @scope public
*/
function isEmpty (prop /* :Object */) /* :Boolean */ {
if (isBoolean(prop)) return false;
if (isRegExp(prop) && new RegExp("").toString() == prop.toString()) return true;
if (isString(prop) || isNumber(prop)) return !prop;
if (Boolean(prop)&&false != prop) {
for (var i in prop) if(prop.hasOwnProperty(i)) return false
}
return true;
}
//-----------------------------------------------------------------------------
// File paths functions
//-----------------------------------------------------------------------------
/**
* used to glue path's
*
* @param {String} number of strings
* @return {String} glued path
* @scope public
*/
function gluePath () /* :String */ {
var aL=arguments.length, i=aL-2, s = arguments[aL-1];
for(;i>=0;i--)
s = ((!isString(arguments[i])&&!isNumber(arguments[i]))||isEmpty(arguments[i])
?s
:arguments[i]+'\x00'+s);
return s?s.replace(/\/*\x00+\/*/g,"/"):"";
}
/**
* return full path to the script
*
* @param {String} sname script name
* @return {String, Null} mixed string full path or null
* @scope public
*/
function findPath (sname /* :String */) /* :String */{
var h =document.getElementsByTagName('html')[0].innerHTML
,sr=new RegExp('<scr'+'ipt[^>]+?src\s*=\s*["\']?([^>]+?/)'+sname+'[^>]*>.?</scr'+'ipt>','i')
,m =h.match(sr);
if (m) {
/*
* we've matched the full path
*/
if (m[1].match(/^((https?|file)\:\/{2,}|\w:[\\])/)) return m[1];
/*
* we've matched absolute path from the site root
*/
if (m[1].indexOf("/")==0) return m[1];
b = document.getElementsByTagName('base');
if (b[0] && b[0].href) return b[0].href+m[1];
/*
* return matching part of the document location and path to js file
*/
return (document.location.href.match(/(.*[\/\\])/)[0]+m[1]).replace(/^\/+/,"");
}
return null;
}
/**
* return parsed query string for the specified script name
*
* @param {String} sname script name
* @return {String, Null} mixed string full path or null
* @scope public
*/
function getScriptQuery (sname) {
var h =document.getElementsByTagName('html')[0].innerHTML
,sr=new RegExp('<scr'+'ipt[^>]+?src\s*[^>]+?/'+sname+'([^#"\']*).+?</scr'+'ipt>','i')
,m = h.match(sr);
if (m) return parseQuery(m[1].replace(/^[^?]*\?([^#]+)/,"$1"));
return {};
}
/**
* Function parses supplied query string and returns the hash with the values
* Multiple values are stored in the array
*
* @param {String} q query string
* @return {Object}
* @scope public
*/
function parseQuery (q) {
if ('string'!=typeof q || q.length<2) return {};
q = q.split(/&amp;|&/g);
for (var z=0,qL=q.length,rs={},kv,rkv;z<qL;z++) {
kv=q[z].split("=");
/*
* convert PHP and Perl-styled hashes to JS has keys
*/
kv[0]=kv[0].replace(/[{}\[\]]*$/,"");
rkv = rs[kv[0]];
/*
* replace all + with spaces, unescape skips this part
*/
kv[1]=unescape(kv[1]?kv[1].replace("+"," "):"");
if (rkv)
if ('array'==typeof(rkv))rs[kv[0]][rs[kv[0]].length]=kv[1];
else rs[kv[0]]=[rs[kv[0]],kv[1]];
else rs[kv[0]]=kv[1];
}
return rs
}
//-----------------------------------------------------------------------------
// Misc helpers
//-----------------------------------------------------------------------------
/**
* Method is used to convert table into the array
*
* @param {String, HTMLTableElement, HTMLTBodyElement, HTMLTHeadElement, HTMLTFootElement} id
* @param {Number} ci column indexes to put in the array
* @param {String} section optional section type
* @param {Object} subsection optional subsection index
* @return {NULL, Array}
* @scope public
*/
function table2array (id, ci, section, subsection) {
if (isString(id)) id = document.getElementById(id);
if (!id || !DOM.hasTagName(id, ['table','tbody,','thead','tfoot'])) return null;
if (!isEmpty(section) && (!isString(section) || !(id = id.getElementsByTagName(section)))) return null;
if (!isEmpty(subsection) && (!isNumber(subsection) || subsection<0 || !(id = id[subsection]))) return null;
if (isUndefined(id.rows)) return null;
var res = []
,span = document.createElement('span')
,ts = null
,ce = null
for (var i=0, rL=id.rows.length; i<rL; i++) {
var tr = [];
if (isArray(ci)) {
for (var z=0, cL=ci.length; z<cL; z++) {
ce = id.rows[i].cells[ci[z]];
if (ce) {
span.innerHTML = ce.innerText?ce.innerText:ce.innerHTML.replace(/<script\s+(.|\r?\n)*?<\/script>|<[^>]*>/g,"");
span.normalize();
tr[tr.length] = span.firstChild?span.firstChild.nodeValue.trim(" \xA0"):"";
} else {
tr[tr.length] = "";
}
}
} else {
for (var z=0, tL=id.rows[i].cells.length; z<tL; z++) {
cd = id.rows[i].cells[z];
span.innerHTML = ce.innerText?ce.innerText:ce.innerHTML.replace(/<script\s+(.|\r?\n)*?<\/script>|<[^>]*>/g,"");
span.normalize();
tr[tr.length] = span.firstChild?span.firstChild.nodeValue.trim(" \xA0"):"";
}
}
if (!isEmpty(tr)) res[res.length] = tr;
}
return res;
}
/**
* Creates element all-at-once
*
* @param {String} tag name
* @param {Object} p element properties { 'class' : 'className',
* 'style' : { 'property' : value, ... },
* 'event' : { 'eventType' : handler, ... },
* 'child' : [ child1, child2, ...],
* 'param' : { 'property' : value, ... },
* @return {HTMLElement} created element or null
* @scope public
*/
document.createElementExt = function (tag /* :String */, p /* :Object */ ) /* :HTMLElement */{
var L, i, k, el = document.createElement(tag);
if (!el) return null;
for (i in p) {
if (!p.hasOwnProperty(i)) continue;
switch (i) {
case "class" : el.setAttribute('className',p[i]); el.setAttribute('class',p[i]); break;
case "style" : for (k in p[i]) { if (!p[i].hasOwnProperty(k)) continue; el.style[k] = p[i][k]; } break;
case "event" : for (k in p[i]) { if (!p[i].hasOwnProperty(k)) continue; el.attachEvent(k,p[i][k]); } break;
case "child" : L = p[i].length; for (k = 0; k<L; k++) el.appendChild(p[i][k]); break;
case "param" : for (k in p[i]) { if (!p[i].hasOwnProperty(k)) continue; try { el[k] = p[i][k] } catch(e) {} } break;
}
}
return el;
}
/**
* simple setInterval/setTimout wrappers
*
* @param {Function} f function to be launched
* @param {Number} i interval
* @param {Array} o optional function parameters to be applied
* @return {Number} interval id
* @scope public
*/
function playInterval (f /* :Function */, i /* :Number */, o /* :Array */) /* :Number */ { return setInterval(function(){(o instanceof Array)?f.apply(this,o):f.call(this,o)},i) }
function playTimeout (f /* :Function */, i /* :Number */, o /* :Array */) /* :Number */ { return setTimeout(function(){(o instanceof Array)?f.apply(this,o):f.call(this,o)},i) }
/**
* Clone object
*
* @param optional object to clone
* @return cloned object
* @access public
*/
function cloneObject (obj) {
if (isScalar(obj) || isFunction(obj) || null == obj) return obj;
try { var newObject = new obj.constructor(); } catch(e) {return null;}
if (isArray(newObject)) {
for (var i=0,oL=obj.length;i<oL;i++) {
newObject[i] = cloneObject(obj[i]);
}
} else {
for (var i in obj) {
if (!obj.hasOwnProperty(i)) continue;
newObject[i] = cloneObject(obj[i]);
}
}
return newObject;
}
/**
* Merges multiple objects to one
*
* @param {Object} obj1 original object
* @param {Object} obj2 update object
* @param {Object} objN update object
* @return {Object}
* @access public
*/
function mergeObject () {
var res = {}
,oi
,obj
for (var z=0,aL=arguments.length;z<aL;z++) {
obj = arguments[z];
for (var i in obj) {
if (!obj.hasOwnProperty(i)) continue;
oi = obj[i];
if (null == oi) {
if (!res.hasOwnProperty(i)) res[i] = oi;
} else if (isArray(oi)) {
if (isArray(res[i]))
res[i] = res[i].concat(oi).unique();
else
res[i] = oi.slice(0);
} else if (isScalar(oi) || isFunction(oi)) {
res[i] = oi;
} else {
if (res.hasOwnProperty(i))
res[i] = mergeObject(res[i],oi)
else
res[i] = cloneObject(oi);
}
}
}
return res;
}
//-----------------------------------------------------------------------------
// Loaders
//-----------------------------------------------------------------------------
/**
* Method to load stylesheets
*
* @param {String} sn path to stylesheet
* @return {HTMLLinkElement} reference to the corresponding link
*/
function loadStyleSheet (sn) {
var head = document.getElementsByTagName('head')[0]
,links = head.getElementsByTagName('link')
,ll = links.length
,cl;
while (--ll>-1) {
cl = links[ll];
if (!cl.rel || cl.rel.toLowerCase() != 'stylesheet' || cl.src != sn) continue;
return cl;
}
var link = document.createElementExt('link',{'param': { 'rel': 'stylesheet', 'type': 'text/css', 'href': sn}})
head.appendChild(link);
return link;
}

View file

@ -0,0 +1,200 @@
/**
* $Id: scriptqueue.js 351 2007-11-13 12:56:18Z wingedfox $
* $HeadURL: https://svn.debugger.ru/repos/jslibs/BrowserExtensions/tags/BrowserExtensions.018/scriptqueue.js $
*
* Dynamically load scripts and script queues (when load order is important)
*
**********NOTE********
* If you need to load any scripts before ScriptQueue exists, use the following snippet
* <code>
* if (!(window.ScriptQueueIncludes instanceof Array)) window.ScriptQueueIncludes = []
* window.ScriptQueueIncludes = window.ScriptQueueIncludes.concat(scriptsarray);
* </code>
* ScriptQueue loads all the scripts, queued before its' load in the ScriptQueueIncludes
**********
*
* @author Ilya Lebedev <ilya@lebedev.net>
* @modified $Date: 2007-11-13 15:56:18 +0300 (Втр, 13 Ноя 2007) $
* @version $Rev: 351 $
* @license LGPL 2.1 or later
*
* @class ScriptQueue
* @param {Function} optional callback function, called on each successful script load
* @scope public
*/
ScriptQueue = function (cbk) {
var self = this
,static = arguments.callee;
/*
* empty function is better than number of checks in the code...
*/
if ('function' != typeof cbk) cbk = function(){}
/**
* Queue for the current loader instance
*
* @type {Array}
* @scope private
*/
var queue = [];
//-------------------------------------------------------------------------
// PUBLIC
//-------------------------------------------------------------------------
/**
* Loads the single script, independent from any other
*
* @param {String} path path to the target script
* @return {Boolean} true when script is getting loaded, false otherwise
* @scope public
*/
self.load = function (path) {
load(path,cbk);
}
/**
* Builds a queue of scripts, when they should be loaded in the proper order
*
* @param {String} path script name to add to the queue
* @scope public
*/
self.queue = function (path) {
var f = queue.length;
queue[f] = path;
if (!f) load(path,queuemonitor);
}
//-------------------------------------------------------------------------
// PRIVATE
//-------------------------------------------------------------------------
/**
* Performs scripts existense check and loads it, if needed
*
* @param {String} path path to the script
* @param {Function} cbk callback
* @scope private
*/
var load = function (path, cbk) {
var sid
,scr = static.scripts;
if (sid = scr.hash[path]) { // in queue
scr = static.scripts[sid]
if (scr[2]) { // loaded
cbk(path,scr[2]);
} else {
scr[1].push(cbk);
}
} else {
sid = scr.length;
scr[sid] = [path,[cbk],false];
scr.hash[path] = sid;
ls(path);
}
}
/**
* Attaches script to the document
*
* @param {String} src path to script
* @scope private
*/
var ls = function (src) {
if (document.body) { // document is loaded, don't use document.write
var s = document.createElement('script')
,h = document.getElementsByTagName("head")[0];
s.type= "text/javascript";
s.charset="UTF-8";
s.src=src;
/*
* some browsers does change the src, store original one here
*/
s.rSrc=src;
s.onload = s.onreadystatechange = loadmonitor;
h.appendChild(s);
} else {
document.write("<scr"+"ipt onload=\"\" src=\""+src+"\" charset=\"UTF-8\"></scr"+"ipt>");
/*
* note, real onload handler is commented out, because IE calls it too late,
* which is not acceptable, because it breaks onload there
* assume, that scripts are loaded successfully
*/
// s.onload = s.onreadystatechange = loadmonitor;
loadmonitor.call({'rSrc':src},{'type':'load'});
}
}
//-------------------------------------------------------------------------
// PROTECTED
//-------------------------------------------------------------------------
/**
* Monitors queue load and runs next iteration, untill empties the queue
*
* @param {String} path loaded script
* @param {Boolean} s load completed status
* @scope protected
*/
var queuemonitor = function (path,s) {
/*
* execute the user callback
*/
cbk(path,s);
queue.splice(0,1);
/*
* next run
*/
if (queue.length && s)
load(queue[0],arguments.callee);
else
cbk(null,s)
}
/**
* Handles onload and onreadystatechange events
*
* @param {Event} e handled event object
* @scope protected
*/
var loadmonitor = function (e) {
var scr = static.scripts
,sid = scr.hash[this.rSrc]
,e = e||window.event
scr = scr[sid];
if (('load' == e.type || 'complete'==this.readyState)) {
if (!scr[2])
scr[2] = true;
else
return; // prevent duplicate calls from event handler
}
for (var i=0,cbk=scr[1],cL=cbk.length;i<cL;i++) {
cbk[i](scr[0],scr[2]);
}
}
}
/**
* Stores information about the loaded scripts
* Element structure:
* [0 : string script path
* 1 : [ array of callback functions ]
* 2 : boolean 'loaded' flag
* ]
* Array fields:
* .hash { maps script paths' to array ids }
*
* @type {Array}
* @scope protected
*/
ScriptQueue.scripts = [false];
ScriptQueue.scripts.hash = {};
/**
* Static method to load bunch of scripts at once
* note, there's no callback support
*
* @param {Array} arr list of scripts to execute
* @scope public
*/
ScriptQueue.queue = function(arr) {
if (!arr.length) return;
var q = new ScriptQueue;
for (var i=0,aL=arr.length;i<aL;i++) {
q.queue(arr[i]);
}
}
/*
* If any load requests does exists, serve them
*/
if (window.ScriptQueueIncludes instanceof Array) {
ScriptQueue.queue(window.ScriptQueueIncludes);
}

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,92 @@
VirtualKeyboard = new function () {
var self = this, to = null;
self.show = self.hide = self.toggle = self.attachInput = function () {
window.status = 'VirtualKeyboard is not loaded yet.';
if (!to) setTimeout(function(){window.status = ''},1000);
}
self.isOpen = function () {
return false;
}
};
(function () {
var pq = function (q) {if ('string'!=typeof q || q.length<2) return {};q = q.split(/&amp;|&/g);for (var z=0,qL=q.length,rs={},kv,rkv;z<qL;z++){kv=q[z].split("=");kv[0]=kv[0].replace(/[{}\[\]]*$/,"");rkv = rs[kv[0]];kv[1]=unescape(kv[1]?kv[1].replace("+"," "):"");if (rkv)if ('array'==typeof(rkv))rs[kv[0]][rs[kv[0]].length]=kv[1];else rs[kv[0]]=[rs[kv[0]],kv[1]];else rs[kv[0]]=kv[1];}return rs}
/*
* track, how we've opened
*/
var targetWindow = window.dialogArguments||window.opener||window.top
,addHead = null
,targetScript = 'vk_loader.js';
try {
if (targetWindow != window) {
var addHead = targetWindow.document.getElementsByTagName('head')[0];
var targetScript = window.location.href.match(/.*\/(.+)\..+$/)[1]+'.js';
}
} catch (e) {
targetWindow = window;
}
q = (function (sname,td){var h=(td||document).getElementsByTagName('html')[0].innerHTML,sr=new RegExp('<scr'+'ipt[^>]+?src[^"\']+.*?'+sname+'([^#"\']*)','i'),m = h.match(sr);if (m) return pq(m[1].replace(/^[^?]*\?([^#]+)/,"$1"));return {};})(targetScript,targetWindow.document)
var p = (function (sname){var sc=document.getElementsByTagName('script'),sr=new RegExp('^(.*/|)('+sname+')([#?]|$)');for (var i=0,scL=sc.length; i<scL; i++) {var m = String(sc[i].src).match(sr);if (m) {if (m[1].match(/^((https?|file)\:\/{2,}|\w:[\\])/)) return m[1];if (m[1].indexOf("/")==0) return m[1];b = document.getElementsByTagName('base');if (b[0] && b[0].href) return b[0].href+m[1];return (document.location.href.match(/(.*[\/\\])/)[0]+m[1]).replace(/^\/+/,"");}}return null;})
('vk_loader.js');
var qs = pq(targetWindow.location.search.slice(1));
var dpd = [ 'extensions/helpers.js'
,'extensions/dom.js'
,'extensions/ext/object.js'
,'extensions/ext/string.js'
,'extensions/ext/regexp.js'
,'extensions/ext/array.js'
,'extensions/eventmanager.js'
,'extensions/documentselection.js'
/*
* not used by default
*
* ,'layouts/unconverted.js'
*/
];
q.skin = qs.vk_skin || q.vk_skin || 'winxp';
q.layout = qs.vk_layout || q.vk_layout || null;
var head = document.getElementsByTagName('head')[0]
,s;
/*
* load styles at the proper places
*/
s = document.createElement('link');
s.rel = 'stylesheet';
s.type= 'text/css';
s.href= p+'css/'+q.skin+'/keyboard.css';
head.appendChild(s);
if (addHead) {
var lnk = targetWindow.document.createElement('link');
lnk.rel = 'stylesheet';
lnk.type= 'text/css';
lnk.href= p+'css/'+q.skin+'/keyboard.css';
addHead.appendChild(lnk);
lnk = null;
}
for (var i=0,dL=dpd.length;i<dL;i++)
dpd[i] = p+dpd[i];
dpd[i++] = p+'virtualkeyboard.js?layout='+q.layout;
dpd[i] = p+'layouts/layouts.js';
if (window.ScriptQueue) {
ScriptQueue.queue(dpd);
} else {
if (!(window.ScriptQueueIncludes instanceof Array)) window.ScriptQueueIncludes = []
window.ScriptQueueIncludes = window.ScriptQueueIncludes.concat(dpd);
/*
* attach script loader
*/
if (document.body) {
s = document.createElement('script');
s.type="text/javascript";
s.src = p+'extensions/scriptqueue.js';
head.appendChild(s);
} else {
document.write("<scr"+"ipt type=\"text/javascript\" src=\""+p+'extensions/scriptqueue.js'+"\"></scr"+"ipt>");
}
}
})();

View file

@ -3,6 +3,9 @@
py:extends="'master.kid'">
<head>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type" py:replace="''"/>
<script type="text/javascript"
src="../static/virtual_keyboard/vk_loader.js?vk_layout=DE German"
py:attrs="src=tg.url('/static/virtual_keyboard/vk_loader.js?vk_layout=DE%20German&amp;vk_skin=soberTouch')" />
<title>Bild versenden</title>
</head>
<body>
@ -20,17 +23,26 @@
<form action="${tg.url('/senden')}" method="post">
<label for="mailaddress">An:</label>
<input type="textfield" id="mailaddress"
name="mailaddress" value="${mailaddress}" size="30" />
name="mailaddress" value="${mailaddress}" size="30"
onfocus="VirtualKeyboard.attachInput(this)" />
<br/>
<label for="mailtext">Text:</label>
<textarea name="mailtext" id="mailtext"
rows="10" cols="40" wrap="physical">${mailtext}</textarea>
rows="10" cols="40" wrap="physical"
onfocus="VirtualKeyboard.attachInput(this)"
>${mailtext}</textarea>
<br/>
<input type="submit" name="senden" value="senden" />
</form>
</div>
</td></tr>
</table>
<div id="vkb"></div>
<script type="text/javascript">
EM.addEventListener(window,'domload',function(){
VirtualKeyboard.toggle('mailaddress','vkb');
});
</script>
</body>
</html>