// KeymanWeb 2.0
// Copyright 2010 Tavultesoft Pty Ltd
/******************************************************************
* Main Keyman Web Module
*
* Code enclosed as an anonymous function to protect name space
*
******************************************************************/
(function()
{
// Declare KeymanWeb, OnScreen Keyboard and Util objects
var keymanweb=window['tavultesoft']['keymanweb'],osk=keymanweb['osk'],util=keymanweb['util'],device=util.device;
/**
* Function debug
* Scope Private
* @param {(string|Object)} s string (or object) to print
* Description Simple debug display (upper right of screen)
*/
keymanweb['debug']=keymanweb.debug=function(s){
var p;//=document.getElementById('debug_output');
if(keymanweb.debugElement == null)
{
var d=document.createElement('DIV'),ds=d.style;
ds.position='absolute';ds.width='30%';ds.maxHeight='50%';ds.top='0';ds.right='0';
ds.minHeight='50px'; ds.border='1px solid blue'; ds.whiteSpace='pre-line';ds.overflowY='scroll';
p=document.createElement('P'); p.id='debug_output';p.style.margin='2px';
d.appendChild(p);
document.body.appendChild(d);
keymanweb.debugElement=p;
}
if((p=document.getElementById('debug_output')) == null) return;
if(arguments.length == 0)
if(typeof p.textContent != 'undefined') p.textContent=''; else p.innerHTML='';
else
{
var ts=new Date().toTimeString().substr(3,5),t=s,m;
if(typeof s == 'object')
{
if(s == null)
{
t='null';
}
else
{
t='{';
for(m in s)
{
t=t+m+':';
switch(typeof s[m])
{
case 'string':
case 'number':
case 'boolean':
t=t+s[m]; break;
default:
t=t+typeof s[m]; break;
}
t=t+'; ';
}
if(t.length > 2) t=t.substr(0,t.length-2); //drop final semi-colon
t+='}';
}
}
if(typeof p.textContent != 'undefined')
p.textContent=ts+' '+t+'\n'+p.textContent;
else
p.innerHTML=ts+' '+t+'
'+p.innerHTML;
}
}
keymanweb.debugElement=null;
var dbg=keymanweb.debug;
/**
* Function addEventListener
* Scope Public
* @param {string} event event to handle
* @param {function(Event)} func event handler function
* @return {boolean} value returned by util.addEventListener
* Description Wrapper function to add and identify KeymanWeb-specific event handlers
*/
keymanweb['addEventListener'] = keymanweb.addEventListener = function(event, func)
{
return util.addEventListener('kmw.'+event, func);
}
/**
* Function setUpTouchDevice
* Scope Private
* Description Initialize event handling and duplicate input fields for touch-input devices
*/
keymanweb.setupTouchDevice = function()
{
/**
* Ideally, OSK behaviour should emulate internal keyboard, but
* it is not possible to do that while allowing native scrolling.
* The compromise adopted is that a touchstart or touchmove event
* on any part of the page other than an input element or the OSK
* itself will temporarily hide the OSK until the touchend or
* window.scroll event is fired. Hiding the OSK in this way seems
* less disturbing than having it move as the page is scrolled.
*
* All of this may be better if we can reliably get the y-offset
* from the CSS transform and apply that to repositioning the OSK
* using a timed event loop.
*/
/**
* Close OSK and remove simulated caret on losing focus
*/
keymanweb.cancelInput = function()
{
keymanweb._ActiveElement=null;
keymanweb.hideCaret();
osk.hideNow();
}
/**
* Handle losing focus from simulated input field
*
* @param {Event} e event
*/
keymanweb.setBlur = function(e)
{
// This works OK for iOS, but may need something else for other platforms
if(('relatedTarget' in e) && e.relatedTarget)
{
if(e.relatedTarget.nodeName != 'DIV' || e.relatedTarget.className.indexOf('keymanweb-input') == -1)
{
keymanweb.cancelInput(); return;
}
}
//Hide the OSK
if(!keymanweb.focusing) keymanweb.cancelInput();
}
/**
* Handle receiving focus by simulated input field
*
* @param {Event=} e event
*/
keymanweb.setFocus=function(e)
{
//e.stopPropagation(); // not doing anything useful, no child elements
//e.preventDefault(); // prevents user selection or scrolling, may be better if they are allowed?
keymanweb.focusing=true;
keymanweb.focusTimer=window.setTimeout(function(){keymanweb.focusing=false;},1000);
var tEvent=e;
if(e)
{
if(typeof e.touches == 'object') tEvent=e.touches[0];
}
// Allow external code to set focus and thus display the OSK on touch devices if required (KMEW-123)
else
{
tEvent={clientX:0,clientY:0}
// Will usually be called from setActiveElement, which should define _LastActiveElement
if(keymanweb._LastActiveElement)
tEvent.target = keymanweb._LastActiveElement['kmw_ip'];
// but will default to first input or text area on page if _LastActiveElement is null
else
tEvent.target = keymanweb.sortedInputs[0]['kmw_ip'];
}
var touchX=tEvent.clientX,touchY=tEvent.clientY,tTarg=tEvent.target,scroller;
// Identify the scroller element
if(tTarg.nodeName == 'SPAN')
scroller=tTarg.parentNode;
else if(tTarg.className != null && tTarg.className.indexOf('keymanweb-input') >= 0)
scroller=tTarg.firstChild;
else
scroller=tTarg;
// And the actual target element
var target=scroller.parentNode;
// Move the caret and refocus if necessary
if(keymanweb._ActiveElement != target)
{
// Hide the KMW caret
keymanweb.hideCaret();
keymanweb._ActiveElement=target;
// The issue here is that touching a DIV does not actually set the focus for iOS, even when enabled to accept focus (by setting tabIndex=0)
// We must explicitly set the focus in order to remove focus from any non-KMW input
target.focus(); //Android native browsers may not like this, but it is needed for Chrome, Safari
}
// What we really want to do is to blur any active element that is not a KMW input,
// but the following line does not work as might be expected, even though the correct element is referenced.
// It is as though blur is ignored if focus is supposed to have been moved, even if it has not in fact been moved?
//if(document.activeElement.nodeName != 'DIV' && document.activeElement.nodeName != 'BODY') document.activeElement.blur();
// Refresh the internal keyboard flag state
keymanweb.useInternalKeyboard=false;
// And display the OSK if not already visible
if(osk.ready && !osk._Visible) osk._Show();
// If clicked on DIV, set caret to end of text
if(tTarg.nodeName == 'DIV' )
{
var x,cp;
x=util._GetAbsoluteX(scroller.firstChild);
if(target.dir == 'rtl')
{
x += scroller.firstChild.offsetWidth;
cp=(touchX > x ? 0 : 100000);
}
else
cp=(touchX touchY && cp > cpMin && cp != cpMax) {cpMax=cp; cp=Math.round((cp+cpMin)/2);}
else if(y < touchY-yRow && cp < cpMax && cp != cpMin) {cpMin=cp; cp=Math.round((cp+cpMax)/2);}
else break;
keymanweb.setTextCaret(target,cp);
}
while(util._GetAbsoluteY(caret)-dy > touchY && cp > cpMin)keymanweb.setTextCaret(target,--cp);
while(util._GetAbsoluteY(caret)-dy < touchY-yRow && cp < cpMax) keymanweb.setTextCaret(target,++cp);
}
// Caret repositioning for horizontal scrolling of RTL text
if(target.dir == 'rtl')
{
for(iLoop=0; iLoop<16; iLoop++)
{
x=util._GetAbsoluteX(caret); //left of caret
if(x < touchX && cp > cpMin && cp != cpMax) {cpMax=cp; cp=Math.round((cp+cpMin)/2);}
else if(x > touchX && cp < cpMax && cp != cpMin) {cpMin=cp; cp=Math.round((cp+cpMax)/2);}
else break;
keymanweb.setTextCaret(target,cp);
}
while(util._GetAbsoluteX(caret) < touchX && cp > cpMin) keymanweb.setTextCaret(target,--cp);
while(util._GetAbsoluteX(caret) > touchX && cp < cpMax) keymanweb.setTextCaret(target,++cp);
}
// Caret repositioning for horizontal scrolling of standard (LTR) text
else
{
for(iLoop=0; iLoop<16; iLoop++) // assumes fields shorter than 2**16 characters
{
x=util._GetAbsoluteX(caret); //left of caret
if(x > touchX && cp > cpMin && cp != cpMax)
{
cpMax=cp; cp=Math.round((cp+cpMin)/2);
}
else if(x < touchX && cp < cpMax && cp != cpMin)
{
cpMin=cp; cp=Math.round((cp+cpMax)/2);
}
else break;
keymanweb.setTextCaret(target,cp);
}
while(util._GetAbsoluteX(caret) > touchX && cp > cpMin) keymanweb.setTextCaret(target,--cp);
while(util._GetAbsoluteX(caret) < touchX && cp < cpMax) keymanweb.setTextCaret(target,++cp);
}
}
// TODO: The following is copied from old control focus... not sure how much is needed
keymanweb._ActiveControl = null;
keymanweb._LastActiveElement = target;
for(var Ln=0; Ln < keymanweb._Controls.length; Ln++) // I1511 - array prototype extended
if(keymanweb._Controls[Ln].LControl == target)
{
keymanweb._ActiveControl = keymanweb._Controls[Ln];
break;
}
if(keymanweb._ActiveControl != null && keymanweb._ActiveControl.LDefaultInternalName != null)
{
if(!keymanweb._JustActivatedKeymanWebUI)
{
keymanweb._SetActiveKeyboard(keymanweb._ActiveControl.LDefaultInternalName,'',true);
}
else
keymanweb._ActiveControl.LDefaultInternalName = keymanweb._ActiveKeyboard == null ? '' : keymanweb._ActiveKeyboard['KI'];
}
//TODO: the logic of the following line doesn't look right!! Both variables are true, but that doesn't make sense!
//_Debug(keymanweb._IsIEEditableIframe(Ltarg,1) + '...' +keymanweb._IsMozillaEditableIframe(Ltarg,1));
if(!keymanweb._IsIEEditableIframe(target,1) || !keymanweb._IsMozillaEditableIframe(target,1))
{
keymanweb._DisableInput = 1; return;
}
keymanweb._DisableInput = 0;
if(!keymanweb._JustActivatedKeymanWebUI)
{
keymanweb._DeadKeys = [];
keymanweb._NotifyKeyboard(0,target,1); // I2187
}
if(!keymanweb._JustActivatedKeymanWebUI && keymanweb._SelectionControl != target)
keymanweb._IsActivatingKeymanWebUI = 0;
keymanweb._JustActivatedKeymanWebUI = 0;
keymanweb._SelectionControl = target;
}
/**
* Create a caret to be appended to the scroller of the focussed input field.
* The caret is appended to the scroller so that it will automatically be clipped
* when the user manually scrolls it outside the element boundaries.
* It is positioned exactly over the hidden span that is inserted between the
* text spans before and after the insertion point.
*/
keymanweb.caret=document.createElement('DIV');
var cs=keymanweb.caret.style;
cs.position='absolute';
cs.height='16px'; // default height, actual height set from element properties
cs.width='2px';
cs.backgroundColor='blue';
cs.border='none';
cs.left=cs.top=0; // actual position set relative to parent when displayed
cs.display='block';
cs.visibility='hidden';
cs.zIndex='9998'; // immediately below the OSK
/**
* Position the caret at the start of the second span within the scroller
*
* @param {Object} e input DIV element (copy of INPUT or TEXTAREA)
*/
keymanweb.showCaret=function(e)
{
if(!e || !e.firstChild || (e.firstChild.childNodes.length<3)) return;
var scroller=e.firstChild,cs=keymanweb.caret.style,sp2=scroller.childNodes[1];
// Attach the caret to this scroller and position it over the caret span
if(keymanweb.caret.parentNode != scroller) scroller.appendChild(keymanweb.caret);
cs.left=sp2.offsetLeft+'px';
cs.top=sp2.offsetTop+'px';
cs.height=(sp2.offsetHeight-1)+'px';
cs.visibility='hidden'; // best to wait for timer to display caret
// Scroll into view if required
keymanweb.scrollBody(e);
// Display and position the scrollbar if necessary
keymanweb.setScrollBar(e);
}
/**
* Display and position a scrollbar in the input field if needed
*
* @param {Object} e input DIV element (copy of INPUT or TEXTAREA)
*/
keymanweb.setScrollBar=function(e)
{
// Display the scrollbar if necessary. Added TEXTAREA condition to correct rotation issue KMW-5. Fixed for 310 beta.
var scroller=e.childNodes[0],sbs=e.childNodes[1].style;
if((scroller.offsetWidth > e.offsetWidth || scroller.offsetLeft < 0) && (e.base.nodeName != 'TEXTAREA'))
{
sbs.height='4px';
sbs.width=100*(e.offsetWidth/scroller.offsetWidth)+'%';
sbs.left=100*(-scroller.offsetLeft/scroller.offsetWidth)+'%';
sbs.top='0';
sbs.visibility='visible';
}
else if(scroller.offsetHeight > e.offsetHeight || scroller.offsetTop < 0)
{
sbs.width='4px';
sbs.height=100*(e.offsetHeight/scroller.offsetHeight)+'%';
sbs.top=100*(-scroller.offsetTop/scroller.offsetHeight)+'%';
sbs.left='0';
sbs.visibility='visible';
}
else
{
sbs.visibility='hidden';
}
}
/**
* Description Toggle state of caret in simulated input field
*/
keymanweb.flashCaret = function()
{
if(device.touchable && keymanweb._ActiveElement != null)
{
var cs=keymanweb.caret.style;
if(cs.visibility != 'visible') cs.visibility='visible'; else cs.visibility='hidden';
}
}
/**
* Hide caret in simulated input field, update underlying INPUT or TEXTAREA
*/
keymanweb.hideCaret = function()
{
var e=keymanweb._LastActiveElement,s=null;
if(e && e.className != null && e.className.indexOf('keymanweb-input') >= 0)
{
// Always copy text back to underlying field on blur
e.base.value = keymanweb.getText(e);
// And set the scroller caret to the end of the element content
keymanweb.setText(e,null,100000);
// Set the element scroll to zero (or max for RTL INPUT)
var ss=e.firstChild.style;
if(e.base.nodeName == 'TEXTAREA')
ss.top='0';
else
{
if(e.base.dir == 'rtl') ss.left=(e.offsetWidth-e.firstChild.offsetWidth-8)+'px';
else ss.left='0';
}
// And hide the caret and scrollbar
if(keymanweb.caret.parentNode) keymanweb.caret.parentNode.removeChild(keymanweb.caret);
keymanweb.caret.style.visibility='hidden';
if(e.childNodes.length > 1 ) e.childNodes[1].style.visibility='hidden';
}
}
// Start the caret flash timer
keymanweb.timerId = window.setInterval(keymanweb.flashCaret,500);
/**
* Insert text into simulated input field at indicated character position
*
* @param {Object} e simulated input field DIV
* @param {?string} t text to insert in element
* @param {?number} cp caret position (characters)
*/
keymanweb.setText = function(e,t,cp)
{
if(e && e.childNodes.length > 0)
{
var d=e.firstChild,tLen=0;
if(d.childNodes.length >= 3)
{
var s1=d.childNodes[0],s2=d.childNodes[2],t1,t2;
// Read current text if null passed (for caret positioning)
if(t === null)
{
t1=(typeof(s1.textContent) == 'string' ? s1.textContent : s1.innerText);
t2=(typeof(s2.textContent) == 'string' ? s2.textContent : s2.innerText);
t=t1+t2;
}
if(cp < 0) cp = 0; //if(typeof t._kmwLength == 'undefined') return;
tLen=t._kmwLength();
if(cp === null || cp > tLen) cp=tLen;
t1=t._kmwSubstr(0,cp); t2=t._kmwSubstr(cp);
if(typeof(s1.textContent) == 'string') s1.textContent=t1; else s1.innerText=t1;
if(typeof(s2.textContent) == 'string') s2.textContent=t2; else s2.innerText=t2;
}
}
keymanweb.updateBaseElement(e,tLen); // KMW-3, KMW-29
}
/**
* Set content, visibility, background and borders of input and base elements (KMW-3,KMW-29)
*
* @param {Object} e input element
* @param {number} n length of text in field
*/
keymanweb.updateBaseElement=function(e,n)
{
e.base.value=keymanweb.getText(e); //KMW-29
e.style.backgroundColor=(n==0?'transparent':window.getComputedStyle(e.base,null).backgroundColor);
if(device.OS == 'iOS')
{
e.base.style.visibility=(n==0?'visible':'hidden');
}
}
/**
* Get simulated input field content
*
* @param {Object} e simulated input field DIV
* @return {string} entire text in simulated input field
*/
keymanweb.getText=function(e)
{
if(e == null) return '';
return (typeof(e.textContent) == 'string' ? e.textContent : e.innerText);
}
/**
* Get text up to the caret from simulated input field
*
* @param {Object} e simulated input field DIV
* @return {string} Context for simulated input field
*/
keymanweb.getTextBeforeCaret=function(e)
{
if(e && e.childNodes.length > 1)
{
var d=e.firstChild;
if(d.childNodes.length > 0)
{
var s1=d.childNodes[0];
if('textContent' in s1)
return s1.textContent;
if('innerText' in s1)
return s1.innerText;
}
}
return '';
}
/**
* Replace text up to the caret in the simulated input field
*
* @param {Object} e simulated input field DIV
* @param {string} t string to insert
*/
keymanweb.setTextBeforeCaret=function(e,t)
{
if(e && e.childNodes.length > 0)
{
var d=e.firstChild,tLen=0;
if(d.childNodes.length > 1)
{
var s1=d.childNodes[0],s2=d.childNodes[2];
// Collapse (trailing) whitespace to a single space for INPUT fields (also prevents wrapping)
if(e.base.nodeName != 'TEXTAREA') t=t.replace(/\s+$/,' ');
if('textContent' in s1) s1.textContent=t;
else if('innerText' in s1) s1.innerText=t;
// Test total length in order to control base element visibility
tLen=t.length;
if('textContent' in s2) tLen=tLen+s2.textContent.length;
else if('innerText' in s2) tLen=tLen+s2.innerText.length;
}
}
// Update the base element then scroll into view if necessary
keymanweb.updateBaseElement(e,tLen); //KMW-3, KMW-29
keymanweb.scrollInput(e);
}
/**
* Description Get current position of caret in simulated input field
*
* @param {Object} e simulated input field DIV
* @return {number} caret character position in simulated input field
*/
keymanweb.getTextCaret=function(e)
{
return keymanweb.getTextBeforeCaret(e)._kmwLength();
}
/**
* Set current position of caret in simulated input field then display the caret
*
* @param {Object} e element (object) or element id (string) of simulated input field
* @param {number} cp caret position (character index)
*/
keymanweb.setTextCaret=function(e,cp)
{
keymanweb.setText(e,null,cp);
keymanweb.showCaret(e);
}
/**
* Handle the touch move event for an input element
*
* @param {Event} e touchmove event
*/
keymanweb.dragInput=function(e)
{
// Prevent dragging window
e.preventDefault(); e.stopPropagation();
// Identify the target from the touch list or the event argument (IE 10 only)
var target=(typeof e.targetTouches == 'object' ? e.targetTouches[0].target : e.target);
if(target == null) return;
// Identify the input element from the touch event target (touched element may be contained by input)
if(target.className == null || target.className.indexOf('keymanweb-input') < 0) target=target.parentNode;
if(target.className == null || target.className.indexOf('keymanweb-input') < 0) target=target.parentNode;
if(target.className == null || target.className.indexOf('keymanweb-input') < 0) return;
var x=(typeof e.touches == 'object' ? e.touches[0].screenX : e.screenX),
y=(typeof e.touches == 'object' ? e.touches[0].screenY : e.screenY);
// Allow content of input elements to be dragged horizontally or vertically
if(typeof keymanweb.firstTouch == 'undefined' || keymanweb.firstTouch == null)
keymanweb.firstTouch={x:x,y:y};
else
{
var x0=keymanweb.firstTouch.x,y0=keymanweb.firstTouch.y,
scroller=target.firstChild,dx,dy,x1;
if(target.base.nodeName == 'TEXTAREA')
{
var yOffset=parseInt(scroller.style.top,10);
if(isNaN(yOffset)) yOffset=0;
dy=y0-y;
if(dy < -4 || dy > 4)
{
scroller.style.top=(yOffset 4)
{
// Limit dragging beyond the defined text (to avoid dragging the text completely out of view)
var xMin=0,xMax=util._GetAbsoluteX(target)+target.offsetWidth-scroller.offsetWidth-32;
if(target.base.dir == 'rtl')xMin=16; else xMax=xMax-24;
x1=xOffset-dx;
if(x1 > xMin) x1=xMin;
if(x1 < xMax) x1=xMax;
scroller.style.left=x1+'px';
keymanweb.firstTouch.x=x;
}
}
}
keymanweb.setScrollBar(target);
}
/**
* Scroll the input field horizontally (INPUT base element) or
* vertically (TEXTAREA base element) to bring the caret into view
* as text is entered or deleted form an element
*
* @param {Object} e simulated input field object with focus
*/
keymanweb.scrollInput=function(e)
{
if(!e || !e.firstChild || e.className == null || e.className.indexOf('keymanweb-input') < 0 ) return;
var scroller=e.firstChild;
if(scroller.childNodes.length < 3) return;
// Get the actual absolute position of the caret and the element
var s2=scroller.childNodes[1],
cx=util._GetAbsoluteX(s2),cy=util._GetAbsoluteY(s2),
ex=util._GetAbsoluteX(e),ey=util._GetAbsoluteY(e),
x=parseInt(scroller.style.left,10),
y=parseInt(scroller.style.top,10),
dx=0,dy=0;
// Scroller offsets must default to zero
if(isNaN(x)) x=0; if(isNaN(y)) y=0;
// Scroll input field vertically if necessary
if(e.base.nodeName == 'TEXTAREA')
{
var rowHeight=Math.round(e.offsetHeight/e.base.rows);
if(cy < ey) dy=cy-ey;
if(cy > ey+e.offsetHeight-rowHeight) dy=cy-ey-e.offsetHeight+rowHeight;
if(dy != 0)scroller.style.top=(y ex+e.offsetWidth-12) dx=cx-ex-e.offsetWidth+12;
if(dx != 0)scroller.style.left=(x x.base.rows+1) x.base.rows=calcRows;
}
ds.width=xs.width; ds.minHeight=xs.height;
}
else
{
ds.minWidth=xs.width; ds.height=xs.height;
}
x.base.style.visibility='hidden'; // hide by default: KMW-3
// Add an explicit event listener to allow the duplicated input element
// to be adjusted for any changes in base element location or size
// This will be called for each element after any rotation, as well as after user-initiated changes
// It has to be wrapped in an anonymous function to preserve scope and be applied to each element.
(function(xx){xx.base.addEventListener('resize',function(e){keymanweb.updateInput(xx);},false);})(x);
// And copy the text content
keymanweb.setText(x,x.base.value,null);
}
}
/*********************************************************
*
* End of main touch-device initialization.
*
*********************************************************/
/**
* Function setupDesktopPage
* Scope Private
* Description Save list of inputs for non-touch devices (desktop browsers)
*/
keymanweb.setupDesktopPage = function()
{
for(var k=0; k<2; k++)
{
var ipList=document.getElementsByTagName(k==0?'INPUT':'TEXTAREA');
for(var n=0;n 0 && ipTextArea.length == 0) n=1;
else if(ipInput.length == 0 && ipTextArea.length > 0) n=2;
else if(ipInput[0].offsetTop < ipTextArea[0].offsetTop) n=1;
else if(ipInput[0].offsetTop > ipTextArea[0].offsetTop) n=2;
else if(ipInput[0].offsetLeft < ipTextArea[0].offsetLeft) n=1;
else if(ipInput[0].offsetLeft > ipTextArea[0].offsetLeft) n=2;
switch(n)
{
case 0:
fs=fsDefault;
case 1:
fs=util.getStyleValue(ipInput[0],'font-family');
case 2:
fs=util.getStyleValue(ipTextArea[0],'font-family');
}
if(typeof(fs) == 'undefined' || fs == 'monospace') fs=fsDefault;
return fs;
}
/**
* Input element stub functions, redefined when a touch device is initialized
*/
/**
* Function stub, for desktop browsers
*
* @param {Event=} e event
*/
keymanweb.setFocus = function(e){};
keymanweb.timerID = null;
/**
* Function stub, for desktop browsers
*
* @param {Object} e element (object) or element id (string) of simulated input field
* @return {string} entire text in simulated input field
*/
keymanweb.getText = function(e){return '';};
/**
* Function stub, for desktop browsers
*
* @param {Object} e element (object) or element id (string) of simulated input field
* @param {?string} t text to insert in element
* @param {?number} cp caret position (characters)
*/
keymanweb.setText = function(e,t,cp){};
/**
* Function stub, for desktop browsers
*
* @return {string}
*/
keymanweb.getTextBeforeCaret = function(){return '';};
/**
* Function stub, for desktop browsers
*
* @param {Object} e element (object) or element id (string) of simulated input field
* @return {string} Context for simulated input field
*/
keymanweb.setTextBeforeCaret = function(e){};
/**
* Function stub, for desktop browsers
*
* @param {Object} e element (object) or element id (string) of simulated input field
* @return {number} caret character position in simulated input field
*/
keymanweb.getTextCaret = function(e){return 0;};
/**
* Function stub, for desktop browsers
*
* @param {Object} e element (object) or element id (string) of simulated input field
* @param {number} cp caret character position in simulated input field
*/
keymanweb.setTextCaret = function(e,cp){};
/**
* Function stub, for desktop browser
*/
keymanweb.hideCaret = function(){};
/**
* Function stub, for desktop browsers
*/
keymanweb.flashCaret = function(){};
/**
* Function stub, for desktop browsers
*
* @param {Object} x element
*/
keymanweb.updateInput=function(x){};
// End of I3363 (Build 301) additions
/**
* Exposed function to load keyboards by name. One or more arguments may be used
*
* @param {string|Object} x keyboard name string or keyboard metadata JSON object
*
*/
keymanweb['addKeyboards'] = function(x)
{
if(arguments.length == 0)
keymanweb.keymanCloudRequest('',false);
else
keymanweb.addKeyboardArray(arguments);
}
/**
* Build 362: addKeyboardArray() link to Cloud. One or more arguments may be used
*
* @param {string|Object} x keyboard name string or keyboard metadata JSON object
*
*/
keymanweb.addKeyboardArray = function(x)
{
// Store all keyboard meta-data for registering later if called before initialization
if(!keymanweb['initialized'])
{
for(var k=0; k 0)
{
var pList=x[i].split('@'),lList=[''],tEntry;
if(pList[0].toLowerCase() == 'english') pList[0] = 'us';
if(pList.length > 1)
{
lList=pList[1].split(',');
}
for(j=0; j 2) tEntry['version']=pList[2];
keymanweb.cloudList.push(tEntry);
}
}
if(typeof(x[i]) == 'object' && x[i] != null)
{
// Register any local keyboards immediately:
// - must specify filename, keyboard name, language codes, region codes
// - no request will be sent to cloud
if(typeof(x[i]['filename']) == 'string')
{
if(!keymanweb.addStub(x[i]))
alert('To use a custom keyboard, you must specify file name, keyboard name, language, language code and region code.');
}
else
{
lList=x[i]['language'];
//Array or single entry?
if(typeof(lList.length) == 'number') {
for(j=0; j 1) && byLanguage ? 'languages' : 'keyboards')
+'?jsonp=tavultesoft.keymanweb.register';
// Set callback timer
tFlag='&timerid='+window.setTimeout(
function(){keymanweb.serverUnavailable(cmd);}
,10000);
Lscript.charset="UTF-8";
Lscript.src = URL+cmd+tFlag;
Lscript.type = 'text/javascript';
try {
document.body.appendChild(Lscript);
}
catch(ex) {
document.getElementsByTagName('head')[0].appendChild(Lscript);
}
}
/**
* Add default or all keyboards for a given language
*
* @param {string} arg Language name (multiple arguments allowed)
**/
keymanweb['addKeyboardsForLanguage'] = function(arg)
{
keymanweb.addLanguageKeyboards(arguments);
}
/**
* Add default or all keyboards for a given language
*
* @param {Object} languages Array of language names
**/
keymanweb.addLanguageKeyboards = function(languages)
{
var i,j,lgName,ll=keymanweb.languageList,cmd,first,addAll;
// Defer registering keyboards by language until the language list has been loaded
if(ll == null)
{
first = (keymanweb.languagesPending.length == 0);
for(i=0; i 9) rIndex = 0; else rIndex = region-1;
}
else if(typeof(region) == 'string')
{
var list = (region.length == 2 ? keymanweb.regionCodes : keymanweb.regions);
for(var i=0; i=0; j--)
{
if('Keyboard_'+arguments[i] == ss[j]['KI'] && ss.length > 1)
{
ss.splice(j,1); break;
}
}
}
// Always reset to the first remaining keyboard
keymanweb._SetActiveKeyboard(ss[0]['KI'],ss[0]['KLC'],true);
// then update the UI keyboard menu
keymanweb.doKeyboardUnregistered();
}
/**
* Browser dependent initialization
*/
if(document.selection) // only defined for IE
{
var appVer=navigator.appVersion;
// Legacy support variables
if(typeof(document.createEventObject)=='undefined' && (appVer.indexOf('MSIE 5.0') >= 0
|| appVer.indexOf('MSIE 4.0') >= 0 || appVer.indexOf('MSIE 3.0') >= 0)) keymanweb.legacy=1;
else if(appVer.indexOf('MSIE 6.0') >= 0) keymanweb._IE = 6;
else if(appVer.indexOf('MSIE 7.0') >= 0) keymanweb._IE = 7;
else if(appVer.indexOf('MSIE 8.0') >= 0) keymanweb._IE = 8;
if(keymanweb._IE && document.compatMode=='BackCompat') keymanweb._IE = 6;
}
// I732 START - Support for European underlying keyboards #1
if(typeof(window['KeymanWeb_BaseLayout']) !== 'undefined')
osk._BaseLayout = window['KeymanWeb_BaseLayout'];
else
osk._BaseLayout = 'us';
keymanweb._BrowserIsSafari = (navigator.userAgent.indexOf('AppleWebKit') >= 0); // I732 END - Support for European underlying keyboards #1
/**
* Function _GetEventObject
* Scope Private
* @param {Event=} e Event object if passed by browser
* @return Event object
* Description Gets the event object from the window when using Internet Explorer
* and handles getting the event correctly in frames
*/
keymanweb._GetEventObject=function(e) // I2404 - Attach to controls in IFRAMEs
{
if (!e)
{
e = window.event;
if(!e)
{
e = keymanweb._GetLastActiveElement();
if(e)
{
e = e.ownerDocument;
if(e) e = e.parentWindow;
if(!e) return null;
e = e.event;
}
}
}
return e;
}
/**
* Function _push
* Scope Private
* @param {Array} Parray Array
* @param {*} Pval Value to be pushed or appended to array
* @return {Array} Returns extended array
* Description Push (if possible) or append a value to an array
*/
keymanweb._push = function(Parray, Pval)
{
if(Parray.push) Parray.push(Pval);
else Parray=Parray.concat(Pval);
return Parray;
}
/**
* Function _IsAttached
* Scope Private
* @param {Object} Pelem Element to be tested
* @return {number} Returns 1 if attached, else 0
* Description Tests whether or not KeymanWeb is attached to element
*/
keymanweb._IsAttached = function(Pelem)
{
for(var i = 0; i < keymanweb._AttachedElements.length; i++)
if(keymanweb._AttachedElements[i] == Pelem) return 1;
return 0;
}
/**
* Function attachToControl
* Scope Public
* @param {Object} Pelem Element to which KMW will be attached
* Description Attaches KMW to control (or IFrame)
*/
keymanweb['attachToControl'] = keymanweb.attachToControl = function(Pelem)
{
// Check that the element is neither readonly nor disabled for KeymanWeb
var ro=Pelem.attributes['readonly'],cn=Pelem.className;
if(typeof ro == 'object' && ro.value != 'false' ) return;
if(typeof cn == 'string' && cn.indexOf('kmw-disabled') >= 0) return;
if(Pelem.tagName.toLowerCase() == 'iframe')
keymanweb._AttachToIframe(Pelem);
else
{
util.attachDOMEvent(Pelem,'focus', keymanweb._ControlFocus);
util.attachDOMEvent(Pelem,'blur', keymanweb._ControlBlur);
Pelem.onkeypress = keymanweb._KeyPress;
Pelem.onkeydown = keymanweb._KeyDown;
Pelem.onkeyup = keymanweb._KeyUp;
}
// I1596 - attach to controls dynamically
if(!keymanweb._IsAttached(Pelem)) keymanweb._AttachedElements.push(Pelem);
}
/**
* Function _AttachToIframe
* Scope Private
* @param {Object} Pelem IFrame to which KMW will be attached
* Description Attaches KeymanWeb to IFrame
*/
keymanweb._AttachToIframe = function(Pelem)
{
try
{
var Lelem=Pelem.contentWindow.document;
/* editable Iframe */
if(Lelem)
{
if(Lelem.parentWindow)
{
// Internet Explorer
if(Lelem.designMode.toLowerCase() == 'on' || Lelem.body.isContentEditable) // I1295 - fix non-attachment for some forms of IFRAMEs
{
// I1480 - Attach to IFRAME instead of document
util.attachDOMEvent(Pelem,'focus', keymanweb._ControlFocus);
util.attachDOMEvent(Pelem,'blur', keymanweb._ControlBlur);
util.attachDOMEvent(Lelem,'keydown', keymanweb._KeyDown); // I2404 - Update for attaching to elements within IFRAMEs, don't attach to read-only IFRAMEs
util.attachDOMEvent(Lelem,'keypress', keymanweb._KeyPress);
util.attachDOMEvent(Lelem,'keyup', keymanweb._KeyUp);
if(!keymanweb.legacy)
{ // I1481 - Attach to the selectionchange in the iframe (and do a selchange to get the new selection)
/* IE: call _SelectionChange when the user changes the selection */
util.attachDOMEvent(Lelem, 'selectionchange', keymanweb._SelectionChange);
keymanweb._SelectionChange();
}
}
}
else
{
if(Lelem.designMode.toLowerCase() == 'on')
{
// Mozilla // I2404 - Attach to IFRAMEs child objects, only editable IFRAMEs here
util.attachDOMEvent(Lelem,'focus', keymanweb._ControlFocus);
util.attachDOMEvent(Lelem,'blur', keymanweb._ControlBlur);
util.attachDOMEvent(Lelem,'keydown', keymanweb._KeyDown);
util.attachDOMEvent(Lelem,'keypress', keymanweb._KeyPress);
util.attachDOMEvent(Lelem,'keyup', keymanweb._KeyUp);
}
else
{
keymanweb._AttachToControls(Lelem); // I2404 - Manage IE events in IFRAMEs
}
}
}
}
catch(err)
{
// do not attempt to attach to the iframe as it is from another domain - XSS denied!
}
}
/**
* Function GetEnabled
* Scope Private
* @return {boolean} True if KMW enabled
* Description Test if KMW enabled
*/
keymanweb.GetEnabled = function()
{
return keymanweb._Enabled;
}
/**
* Function SetEnabled
* Scope Private
* @param {(boolean|number)} Pvalue True to enable KMW
* Description Enable or disable KMW
*/
keymanweb.SetEnabled = function(Pvalue)
{
if(Pvalue) Pvalue=1; else Pvalue=0;
if(keymanweb._Enabled != Pvalue)
{
keymanweb._Enabled = Pvalue;
if((!Pvalue) && keymanweb['HideInterface']) keymanweb['HideInterface'](); //JMD 3/9/10
}
}
/**
* Function DisableControl
* Scope Private
* @param {Object} Pelem Element to be disabled
* Description Disable KMW control element
*/
keymanweb.DisableControl = function(Pelem)
{
var Ln, Lc;
for(Ln=0; Ln 1 && setFocus)
{
if(device.touchable) keymanweb.setFocus();
else keymanweb['focusLastActiveElement']();
}
}
/**
* Function getUIState
* Scope Public
* @return {Object.}
* Description Return object with activation state of UI:
* activationPending (bool): KMW being activated
* activated (bool): KMW active
*/
keymanweb['getUIState'] = keymanweb.getUIState = function()
{
var p={};
p['activationPending'] = p.activationPending = keymanweb._IsActivatingKeymanWebUI;
p['activated'] = p.activated = keymanweb._JustActivatedKeymanWebUI;
return p;
}
/**
* Set or clear the IsActivatingKeymanWebUI flag (exposed function)
*
* @param {(boolean|number)} state Activate (true,false)
*/
keymanweb['activatingUI'] = function(state)
{
keymanweb._IsActivatingKeymanWebUI = (state ? 1 : 0);
}
//TODO: add more complete description of what ControlFocus really does
/**
* Respond to KeymanWeb-aware input element receiving focus
*
* @param {Event} e Event object
* @return {boolean} always true (?)
*/
keymanweb._ControlFocus = function(e)
{
var Ltarg, Ln;
if(!keymanweb._Enabled) return true;
e = keymanweb._GetEventObject(e); // I2404 - Manage IE events in IFRAMEs
if(!e) return true;
if (e.target) Ltarg = e.target;
else if (e.srcElement) Ltarg = e.srcElement;
else return true;
// Prevent any action if a protected input field
if(device.touchable && (Ltarg.className == null || Ltarg.className.indexOf('keymanweb-input') < 0)) return true;
// Or if not a remappable input field
var en=Ltarg.nodeName.toLowerCase();
if(en == 'input')
{
var et=Ltarg.type.toLowerCase();
if(!(et == 'text' || et == 'search')) return true;
}
else if(en != 'textarea') return true;
keymanweb._ActiveElement=Ltarg; // I3363 (Build 301)
if (Ltarg.nodeType == 3) // defeat Safari bug
Ltarg = Ltarg.parentNode;
var LfocusTarg = Ltarg;
// Ensure that focussed element is visible above the keyboard
if(device.touchable && (Ltarg.className == null || Ltarg.className.indexOf('keymanweb-input') < 0)) keymanweb.scrollBody(Ltarg);
if(Ltarg.tagName=='IFRAME') //**TODO: check case reference
{
keymanweb._AttachToIframe(Ltarg);
Ltarg=Ltarg.contentWindow.document;
}
//??keymanweb._Selection = null;
keymanweb._ActiveControl = null;
keymanweb._LastActiveElement = Ltarg;
for(Ln=0; Ln < keymanweb._Controls.length; Ln++) // I1511 - array prototype extended
if(keymanweb._Controls[Ln].LControl == Ltarg)
{
keymanweb._ActiveControl = keymanweb._Controls[Ln];
break;
}
if(keymanweb._ActiveControl != null && keymanweb._ActiveControl.LDefaultInternalName != null)
{
if(!keymanweb._JustActivatedKeymanWebUI)
{
keymanweb._SetActiveKeyboard(keymanweb._ActiveControl.LDefaultInternalName,'',true);
}
else
keymanweb._ActiveControl.LDefaultInternalName = keymanweb._ActiveKeyboard == null ? '' : keymanweb._ActiveKeyboard['KI'];
}
//TODO: the logic of the following line doesn't look right!! Both variables are true, but that doesn't make sense!
//_Debug(keymanweb._IsIEEditableIframe(Ltarg,1) + '...' +keymanweb._IsMozillaEditableIframe(Ltarg,1));
if(!keymanweb._IsIEEditableIframe(Ltarg,1) || !keymanweb._IsMozillaEditableIframe(Ltarg,1))
{
keymanweb._DisableInput = 1;
return true;
}
keymanweb._DisableInput = 0;
if(!keymanweb._JustActivatedKeymanWebUI)
{
keymanweb._DeadKeys = [];
keymanweb._NotifyKeyboard(0,Ltarg,1); // I2187
}
if(!keymanweb._JustActivatedKeymanWebUI && keymanweb._SelectionControl != Ltarg)
keymanweb._IsActivatingKeymanWebUI = 0;
keymanweb._JustActivatedKeymanWebUI = 0;
keymanweb._SelectionControl = Ltarg;
Ltarg._KeymanWebSelectionStart = Ltarg._KeymanWebSelectionEnd = null; // I3363 (Build 301)
// Set element directionality (but only if element is empty)
keymanweb._SetTargDir(Ltarg);
//Execute external (UI) code needed on focus if required
keymanweb.doControlFocused(LfocusTarg,keymanweb._ActiveControl);
// Force display of OSK for touch input device, or if a CJK keyboard, to ensure visibility of pick list
if(device.touchable)
{
osk._Enabled=1;
}
else
{
// Conditionally show the OSK when control receives the focus
if(osk.ready)
{
if(keymanweb.isCJK()) osk._Enabled=1;
if(osk._Enabled) osk._Show(); else osk._Hide(false);
}
}
// TODO: This is possibly the main sequencing issue, as ControlFocus may get called before other elements
// are ready, or keyboards downloaded. Need some way to ensure that the OSK is displayed when everything is ready.
// We have slightly different focus management on IE to other browsers. This is because
// IE has problems with loss of focus when clicking on another element. This can probably
// be resolved in the future by using MouseDown and MouseUp events instead of Click events
// on the VK elements but for now we just use the smoother interaction on Firefox and Chrome,
// and let IE do the focus dance. This means some careful management of the
// IsActivatingKeymanWebUI and LastActiveElement variables, so be careful with any changes
// to these.
// if(keymanweb._IE) keymanweb._LastActiveElement = null; // I2498 - KeymanWeb OSK does not accept clicks in FF when using automatic UI
return true;
}
/**
* Function _IsIEEditableIframe
* Scope Private
* @param {Object} Pelem Iframe element
* {boolean|number} PtestOn 1 to test if frame content is editable (TODO: unclear exactly what this is doing!)
* @return {boolean}
* Description Test if element is an IE editable IFrame
*/
keymanweb._IsIEEditableIframe = function(Pelem,PtestOn)
{
var Ldv, Lvalid = Pelem && (Ldv=Pelem.tagName) && Ldv.toLowerCase() == 'body' && (Ldv=Pelem.ownerDocument) && Ldv.parentWindow;
return (!PtestOn && Lvalid) || (PtestOn && (!Lvalid || Pelem.isContentEditable));
}
/**
* Function _IsMozillaEditableIframe
* Scope Private
* @param {Object} Pelem Iframe element
* @param {boolean|number} PtestOn 1 to test if 'designMode' is 'ON'
* @return {boolean}
* Description Test if element is a Mozilla editable IFrame
*/
keymanweb._IsMozillaEditableIframe = function(Pelem,PtestOn)
{
var Ldv, Lvalid = Pelem && (Ldv=Pelem.defaultView) && Ldv.frameElement;
return (!PtestOn && Lvalid) || (PtestOn && (!Lvalid || Ldv.document.designMode.toLowerCase()=='on'));
}
/**
* Respond to KMW losing focus on event
*
* @param {Event} e Event object
* @return {boolean} Always true (?)
*/
keymanweb._ControlBlur = function(e)
{
var Ltarg;
if(!keymanweb._Enabled) return true;
e = keymanweb._GetEventObject(e); // I2404 - Manage IE events in IFRAMEs
if(!e) return true;
if (e.target) Ltarg = e.target;
else if (e.srcElement) Ltarg = e.srcElement;
else return true;
keymanweb._ActiveElement = null; // I3363 (Build 301)
// Hide the touch device input caret, if applicable I3363 (Build 301)
if(device.touchable) keymanweb.hideCaret();
if (Ltarg.nodeType == 3) // defeat Safari bug
Ltarg = Ltarg.parentNode;
if(Ltarg.tagName=='IFRAME')
Ltarg=Ltarg.contentWindow.document;
if (Ltarg.setSelectionRange)
{
//Ltarg._KeymanWebSelectionStart = Ltarg.selectionStart;
//Ltarg._KeymanWebSelectionEnd = Ltarg.selectionEnd;
Ltarg._KeymanWebSelectionStart = Ltarg.value._kmwCodeUnitToCodePoint(Ltarg.selectionStart); //I3319
Ltarg._KeymanWebSelectionEnd = Ltarg.value._kmwCodeUnitToCodePoint(Ltarg.selectionEnd); //I3319
}
////keymanweb._SelectionControl = null;
keymanweb._LastActiveElement = Ltarg;
if(keymanweb._ActiveControl != null && keymanweb._ActiveControl.LDefaultInternalName != null)
if(keymanweb._ActiveKeyboard == null)
keymanweb._ActiveControl.LDefaultInternalName = '';
else
keymanweb._ActiveControl.LDefaultInternalName = keymanweb._ActiveKeyboard['KI'];
keymanweb._ActiveControl = null;
if(!keymanweb._IsActivatingKeymanWebUI) keymanweb._NotifyKeyboard(0,Ltarg,0); // I2187
e = keymanweb._GetEventObject(e); // I2404 - Manage IE events in IFRAMEs //TODO: is this really needed again????
keymanweb.doControlBlurred(Ltarg,e,keymanweb._IsActivatingKeymanWebUI);
// Hide the OSK when the control is blurred, unless the UI is being temporarily selected
if(osk.ready && !keymanweb._IsActivatingKeymanWebUI) osk._Hide(false);
return true;
}
/****************************************************************
*
* Provide for external processing on events
*
***************************************************************/
/**
* Function doControlBlurred
* Scope Private
* @param {Object} _target element losing focus
* @param {Event} _event event object
* @param {(boolean|number)} _isActivating activation state
* @return {boolean}
* Description Execute external (UI) code needed on blur
*/
keymanweb.doControlBlurred = function(_target,_event,_isActivating)
{
var p={}; p['target']=_target; p['event']=_event; p['isActivating']=_isActivating;
return util.callEvent('kmw.controlblurred',p);
}
/**
* Function doControlFocused
* Scope Private
* @param {Object} _target element gaining focus
* @param {Object} _activeControl currently active control
* @return {boolean}
* Description Execute external (UI) code needed on focus
*/
keymanweb.doControlFocused = function(_target,_activeControl)
{
var p={}; p['target']=_target; p['activeControl']=_activeControl;
return util.callEvent('kmw.controlfocused',p);
}
/**
* Execute external (UI) code needed on registering keyboard, used
* to update each UIs language menu
*
* Note that the argument object is not at present used by any UI,
* since the menu is always fully recreated when needed, but the arguments
* remain defined to allow for possible use in future (Aug 2014)
*
* @param {string} _internalName
* @param {string} _language
* @param {string} _keyboardName
* @param {string} _languageCode
* @return {boolean}
*/
keymanweb.doKeyboardRegistered = function(_internalName,_language,_keyboardName,_languageCode)
{
var p={'internalName':_internalName,'language':_language,'keyboardName':_keyboardName,'languageCode':_languageCode};
return util.callEvent('kmw.keyboardregistered',p);
}
/**
* Execute external (UI) code to rebuild menu when deregistering keyboard
*
* @return {boolean}
*/
keymanweb.doKeyboardUnregistered = function()
{
var p={};
return util.callEvent('kmw.keyboardregistered',p);
}
/**
* Execute external (UI) code needed on laoding keyboard
*
* @param {string} _internalName
* @return {boolean}
*/
keymanweb.doKeyboardLoaded = function(_internalName)
{
var p={}; p['keyboardName']=_internalName;
return util.callEvent('kmw.keyboardloaded',p);
}
/**
* Function doBeforeKeyboardChange
* Scope Private
* @param {string} _internalName
* @param {string} _languageCode
* @return {boolean}
* Description Execute external (UI) code needed before changing keyboard
*/
keymanweb.doBeforeKeyboardChange = function(_internalName,_languageCode)
{
var p={}; p['internalName']=_internalName; p['languageCode']=_languageCode;
return util.callEvent('kmw.beforekeyboardchange',p);
}
/**
* Execute external (UI) code needed *after* changing keyboard
*
* @param {string} _internalName
* @param {string} _languageCode
* @param {boolean=} _indirect
* @return {boolean}
*/
keymanweb.doKeyboardChange = function(_internalName,_languageCode,_indirect)
{
var p={}; p['internalName']=_internalName; p['languageCode']=_languageCode;
p['indirect']=(arguments.length > 2 ? _indirect : false);
return util.callEvent('kmw.keyboardchange',p);
}
/**
* Function doUnloadOSK
* Scope Private
* @return {boolean}
* Description Execute external (UI) code if any needed after unloading OSK (probably not required)
*/
keymanweb.doUnloadOSK = function()
{
var p={};
return util.callEvent('kmw.unloadosk',p);
}
/**
* Function doLoadUI
* Scope Private
* @return {boolean}
* Description Execute UI initialization code after loading the UI
*/
keymanweb.doLoadUI = function()
{
var p={};
return util.callEvent('kmw.loaduserinterface',p);
}
/**
* Function doUnloadUI
* Scope Private
* @return {boolean}
* Description Execute UI cleanup code before unloading the UI (may not be required?)
*/
keymanweb.doUnloadUI = function()
{
var p={};
return util.callEvent('kmw.unloaduserinterface',p);
}
/*****************************************************************************
*
* Provide for handling the initial focus event differently
* The first focus event can happen before we get the WindowLoad,
* e.g. if the page activates a control on WindowLoad itself,
* so trap that and run it through to the page
*
*****************************************************************************/
/**
* Function _BubbledFocus
* Scope Private
* @param {Event} e Event object
* Description Respond to KMW receiving bubbled focus on event (TODO: may not be needed, not currently doing anything)
*/
keymanweb._BubbledFocus = function(e) { /*KeymanWeb._WindowLoad(e);*/ }
if (window.addEventListener)
window.addEventListener('focus', keymanweb._BubbledFocus, true);
/**
* Function _GetKeyEventProperties
* Scope Private
* @param {Event} e Event object
* @return {Object.} KMW keyboard event object:
* Description Get object with target element, key code, shift state, virtual key state
* Ltarg=target element
* Lcode=keyCode
* Lmodifiers=shiftState
* LisVirtualKeyCode e.g. ctrl/alt key
* LisVirtualKey e.g. Virtual key or non-keypress event
*/
keymanweb._GetKeyEventProperties = function(e)
{
var s = new Object();
e = keymanweb._GetEventObject(e); // I2404 - Manage IE events in IFRAMEs
if(!e) return null;
if(e.cancelBubble === true) return null; // I2457 - Facebook meta-event generation mess -- two events generated for a keydown in Facebook contentEditable divs
if (e.target) s.Ltarg = e.target;
else if (e.srcElement) s.Ltarg = e.srcElement;
else return null;
if (s.Ltarg.nodeType == 3) // defeat Safari bug
s.Ltarg = s.Ltarg.parentNode;
if (e.keyCode) s.Lcode = e.keyCode;
else if (e.which) s.Lcode = e.which;
else return null;
s.Lmodifiers =
(e.shiftKey ? 0x10 : 0) |
(e.ctrlKey ? (e.ctrlLeft ? 0x20 : 0x20) : 0) |
(e.altKey ? (e.altLeft ? 0x40 : 0x40) : 0); // I3363 (Build 301)
//s.LisVirtualKey = (e.charCode != null && (e.charCode == 0 || (s.Lmodifiers & 0x60) != 0)) || e.type != 'keypress';
//s.LisVirtualKeyCode = false;
s.LisVirtualKeyCode = (typeof e.charCode != 'undefined' && e.charCode != null && (e.charCode == 0 || (s.Lmodifiers & 0x60) != 0));
s.LisVirtualKey = s.LisVirtualKeyCode || e.type != 'keypress';
return s;
}
/**
* Function _SelectionChange
* Scope Private
* @return {boolean}
* Description Respond to selection change event
*/
keymanweb._SelectionChange = function()
{
if(keymanweb._IgnoreNextSelChange)
{
keymanweb._IgnoreNextSelChange--;
}
else
{
var Ls=document.selection;
if(Ls.type.toLowerCase()!='control') // && document.selection.createRange().parentElement() == keymanweb._SelectionControl) // && window.event.srcElement == keymanweb._SelectionControl)
{
var Lrange=Ls.createRange();
if(!keymanweb._Selection || !keymanweb._Selection.isEqual(Lrange))
{
keymanweb._Selection = Lrange;
/* Delete deadkeys for IE when certain keys pressed */
keymanweb._DeadKeys = [];
}
}
}
return true;
}
// TODO: find out if _FindCaret is still needed for current FireFox, delete if not
/**
* Function _FindCaret
* Scope Private
* @param {Object} Pelem element
* Description Work around a problem with scrolling text boxes and input boxes in Firefox, not needed for other browsers
*/
keymanweb._FindCaret = function(Pelem) // I779
{
if(!Pelem.createTextRange && Pelem.selectionStart)
{
var Levent=document.createEvent('KeyboardEvent');
if(Levent.initKeyEvent)
{
Levent.initKeyEvent('keypress',true,true,null,false,false,false,false,0,32);
var LkeyPress = keymanweb._KeyPress;
/**
* Function _KeyPress
* Scope Private
* @param {Event} e event
* Description Temporarily disable keypress event handling TODO: does this really work??? objects are passed by reference so should be OK
*/
keymanweb._KeyPress = function(e) {};
Pelem.dispatchEvent(Levent);
Levent=document.createEvent('KeyboardEvent');
Levent.initKeyEvent('keypress',true,true,null,false,false,false,false,8,0);
Pelem.dispatchEvent(Levent);
keymanweb._KeyPress = LkeyPress;
}
}
}
/**
* Function _NotifyKeyboard
* Scope Private
* @param {number} _PCommand event code (16,17,18) or 0
* @param {Object} _PTarget target element
* @param {number} _PData 1 or 0
* Description Notifies keyboard of keystroke or other event
*/
keymanweb._NotifyKeyboard = function(_PCommand,_PTarget,_PData) // I2187
{
if(keymanweb._ActiveKeyboard != null && typeof(keymanweb._ActiveKeyboard['KNS']) == 'function') keymanweb._ActiveKeyboard['KNS'](_PCommand,_PTarget,_PData);
}
/**
* Function _KeyDown
* Scope Private
* @param {Event} e event
* @return {boolean}
* Description Processes keydown event and passes data to keyboard
*/
keymanweb._KeyDown = function(e)
{
var Ldv,eClass='';
keymanweb._KeyPressToSwallow = 0;
if(!keymanweb._Enabled || keymanweb._DisableInput || keymanweb._ActiveKeyboard == null ||
(keymanweb._ActiveControl != null && !keymanweb._ActiveControl.LEnabled)) return true;
// Prevent mapping element is readonly or tagged as kmw-disabled
var el=util.eventTarget(e);
if(device.touchable)
{
if(el && typeof el.kmwInput != 'undefined' && el.kmwInput == false) return true;
}
else if(el && el.className.indexOf('kmw-disabled') >= 0) return true;
// Or if OSK not yet ready (for any reason)
if(!osk.ready) return true;
// Get event properties
var Levent = keymanweb._GetKeyEventProperties(e);
if(Levent == null) return true;
switch(Levent.Lcode)
{
case 8:
keymanweb._DeadKeys = [];
break; // I3318 (always clear deadkeys after backspace)
case 16:
case 17:
case 18:
keymanweb._NotifyKeyboard(Levent.Lcode,Levent.Ltarg,1);
return osk._UpdateVKShift(Levent, Levent.Lcode-15, 1); // I2187
}
// I1207
if((Ldv=Levent.Ltarg.ownerDocument) && (Ldv=Ldv.selection) && (Levent.Lcode<33 || Levent.Lcode>40))
{
Ldv.createRange().select();
}
if(!window.event)
{
// I1466 - Convert the - keycode on mnemonic as well as positional layouts
// FireFox, Mozilla Suite
if(keymanweb._VKMap_FF_IE['k'+Levent.Lcode]) Levent.Lcode=keymanweb._VKMap_FF_IE['k'+Levent.Lcode];
}
//else
//{
// Safari, IE, Opera?
//}
if(!keymanweb._ActiveKeyboard['KM'])
{
// Positional Layout
var LeventMatched=0;
/* 13/03/2007 MCD: Swedish: Start mapping of keystroke to US keyboard */
var Lbase=keymanweb._VKMap[osk._BaseLayout];
if(Lbase && Lbase['k'+Levent.Lcode]) Levent.Lcode=Lbase['k'+Levent.Lcode];
/* 13/03/2007 MCD: Swedish: End mapping of keystroke to US keyboard */
if(typeof(keymanweb._ActiveKeyboard['KM'])=='undefined' && !(Levent.Lmodifiers & 0x60))
{
// Support version 1.0 KeymanWeb keyboards that do not define positional vs mnemonic
var Levent2={Lcode:keymanweb._USKeyCodeToCharCode(Levent),Ltarg:Levent.Ltarg,Lmodifiers:0,LisVirtualKey:0};
if(keymanweb.callKeyboardStartGroup(Levent2.Ltarg,Levent2)) LeventMatched=1;
}
LeventMatched = LeventMatched || keymanweb.callKeyboardStartGroup(Levent.Ltarg,Levent);
// Support backspace in simulated input DIV from physical keyboard where not matched in rule I3363 (Build 301)
if(Levent.Lcode == 8 && !LeventMatched && Levent.Ltarg.className != null && Levent.Ltarg.className.indexOf('keymanweb-input') >= 0)
keymanweb.KO(1,keymanweb._LastActiveElement,"");
}
else
{
// Mnemonic layout
if(Levent.Lcode == 8) // I1595 - Backspace for mnemonic
{
keymanweb._KeyPressToSwallow = 1;
if(!keymanweb.callKeyboardStartGroup(Levent.Ltarg,Levent))
keymanweb.KO(1,keymanweb._LastActiveElement,""); // I3363 (Build 301)
return false; //added 16/3/13 to fix double backspace on mnemonic layouts on desktop
}
else
keymanweb._KeyPressToSwallow = 0;
}
if(!LeventMatched && Levent.Lcode >= 96 && Levent.Lcode <= 111)
{
// Number pad, numlock on
// _Debug('KeyPress NumPad code='+Levent.Lcode+'; Ltarg='+Levent.Ltarg.tagName+'; LisVirtualKey='+Levent.LisVirtualKey+'; _KeyPressToSwallow='+keymanweb._KeyPressToSwallow+'; keyCode='+(e?e.keyCode:'nothing'));
if(Levent.Lcode < 106) var Lch = Levent.Lcode-48;
else Lch = Levent.Lcode-64;
keymanweb.KO(0, Levent.Ltarg, String._kmwFromCharCode(Lch)); //I3319
LeventMatched = 1;
}
if(LeventMatched)
{
keymanweb._FindCaret(Levent.Ltarg); //I779
if(e && e.preventDefault) e.preventDefault();
keymanweb._KeyPressToSwallow = (e?e.keyCode:0);
return false;
}
else keymanweb._KeyPressToSwallow = 0;
if(Levent.Lcode == 8)
{
/* Backspace - delete deadkeys, also special rule if desired? */
// This is needed to prevent jumping to previous page, but why??? // I3363 (Build 301)
if(Levent.Ltarg.className != null && Levent.Ltarg.className.indexOf('keymanweb-input') >= 0) return false;
}
return true;
}
keymanweb.callKeyboardStartGroup = function(Ltarg, Levent) {
keymanweb._CachedSelectionStart = null; // I3319
keymanweb._DeadkeyResetMatched(); // I3318
keymanweb.cachedContext.reset();
return keymanweb._ActiveKeyboard['gs'](Ltarg, Levent);
}
/**
* Function _KeyPress
* Scope Private
* @param {Event} e event
* @return {boolean}
* Description Processes keypress event (does not pass data to keyboard)
*/
keymanweb._KeyPress = function(e)
{
var Levent;
if(!keymanweb._Enabled || keymanweb._DisableInput || keymanweb._ActiveKeyboard == null ||
(keymanweb._ActiveControl != null && !keymanweb._ActiveControl.LEnabled)) return true;
Levent = keymanweb._GetKeyEventProperties(e);
if(Levent == null || Levent.LisVirtualKey) return true;
// _Debug('KeyPress code='+Levent.Lcode+'; Ltarg='+Levent.Ltarg.tagName+'; LisVirtualKey='+Levent.LisVirtualKey+'; _KeyPressToSwallow='+keymanweb._KeyPressToSwallow+'; keyCode='+(e?e.keyCode:'nothing'));
/* I732 START - 13/03/2007 MCD: Swedish: Start positional keyboard layout code: prevent keystroke */
if(!keymanweb._ActiveKeyboard['KM'])
{
if(!keymanweb._KeyPressToSwallow) return true;
if(Levent.Lcode < 0x20 || (keymanweb._BrowserIsSafari && (Levent.Lcode > 0xF700 && Levent.Lcode < 0xF900))) return true;
e = keymanweb._GetEventObject(e); // I2404 - Manage IE events in IFRAMEs
if(e) e.returnValue = false;
return false;
}
/* I732 END - 13/03/2007 MCD: Swedish: End positional keyboard layout code */
if(keymanweb._KeyPressToSwallow || keymanweb.callKeyboardStartGroup(Levent.Ltarg,Levent))
{
keymanweb._KeyPressToSwallow=0;
if(e && e.preventDefault) e.preventDefault();
keymanweb._FindCaret(Levent.Ltarg); // I779
return false;
}
keymanweb._KeyPressToSwallow=0;
return true;
}
/**
* Function _KeyUp
* Scope Private
* @param {Event} e event
* @return {boolean}
* Description Processes keyup event and passes event data to keyboard
*/
keymanweb._KeyUp = function(e)
{
var Levent = keymanweb._GetKeyEventProperties(e);
if(Levent == null || !osk.ready) return true;
switch(Levent.Lcode)
{
case 13:
if(Levent.Ltarg.nodeName == 'TEXTAREA') break;
// For input fields, move to next input element
if(Levent.Ltarg.type == 'search' || Levent.Ltarg.type == 'submit')
Levent.Ltarg.form.submit();
else
keymanweb.moveToNext(false);
return true;
case 16:
case 17:
case 18: keymanweb._NotifyKeyboard(Levent.Lcode,Levent.Ltarg,0); return osk._UpdateVKShift(Levent, Levent.Lcode-15, 1); // I2187
}
// I736 start
var Ldv;
if((Ldv=Levent.Ltarg.ownerDocument) && (Ldv=Ldv.selection) && Ldv.type != 'control') // I1479 - avoid createRange on controls
{
Ldv=Ldv.createRange();
//if(Ldv.parentElement()==Levent.Ltarg) //I1505
keymanweb._Selection = Ldv;
}
// I736 end
//if(document.selection && document.selection.type=='Text') keymanweb._Selection=document.selection.createRange();
//if(!KeymanWeb._Enabled) return true;
//if (!e) e = window.event;
return false;
}
//TODO: check return of _KeyUp - what happens if returning true or false ?? what if null returned?
/**
* Move focus to next (or previous) input or text area element on TAB
* Uses list of actual input elements
*
* Note that _ActiveElement on touch devices returns the DIV that overlays
* the input element, not the element itself.
*
* @param {number|boolean} bBack Direction to move (0 or 1)
*/
keymanweb.moveToNext=function(bBack)
{
var i,t=keymanweb.sortedInputs,
activeBase=keymanweb._ActiveElement;
// For touchable devices, get the base element of the DIV
if(device.touchable) activeBase=activeBase.base;
// Identify the active element in the list of inputs ordered by position
for(i=0; i= t.length) i=i-t.length;
if(i < 0) i=i+t.length;
// Move to the selected element
if(device.touchable)
{
// Set focusing flag to prevent OSK disappearing
keymanweb.focusing=true;
var target=t[i]['kmw_ip'];
// Focus if next element is non-mapped
if(typeof(target) == 'undefined')
{
t[i].focus();
}
// Or reposition the caret on the input DIV if mapped
else
{
keymanweb._ActiveElement=keymanweb._LastActiveElement=target;
keymanweb.setTextCaret(target,10000);
keymanweb.scrollInput(target);
}
}
// Behaviour for desktop browsers
else
{
t[i].focus();
}
}
/**
* Move focus to user-specified element
*
* @param {string|Object} e element or element id
*
**/
keymanweb['moveToElement'] = function(e)
{
var i;
if(typeof(e) == 'string') e=document.getElementById(e);
if(device.touchable && e['kmw_ip'])
e['kmw_ip'].focus();
else
e.focus();
}
/**
* Change active keyboard to keyboard selected by (internal) name and language code
*
* Test if selected keyboard already loaded, and simply update active stub if so.
* Otherwise, insert a script to download and insert the keyboard from the repository
* or user-indicated file location.
*
* @param {string} PInternalName
* @param {string=} PLgCode
* @param {boolean=} saveCookie
*/
keymanweb._SetActiveKeyboard = function(PInternalName,PLgCode,saveCookie)
{
var n,Ln,lgCode;
// Set default language code
if(arguments.length < 2 || (!PLgCode)) lgCode='---'; else lgCode=PLgCode;
// Check that the saved keyboard is currently registered
for(n=0; n= keymanweb._KeyboardStubs.length))
{
PInternalName=keymanweb._KeyboardStubs[0]['KI']; lgCode=keymanweb._KeyboardStubs[0]['KLC'];
}
// Save name of keyboard (with language code) as a cookie
if(arguments.length > 2 && saveCookie) keymanweb.saveCurrentKeyboard(PInternalName,lgCode);
// Check if requested keyboard and stub are currently active
if(keymanweb._ActiveStub && keymanweb._ActiveKeyboard
&& keymanweb._ActiveKeyboard['KI'] == PInternalName
&& keymanweb._ActiveStub['KI'] == PInternalName //this part of test should not be necessary, but keep anyway
&& keymanweb._ActiveStub['KLC'] == PLgCode
&& !keymanweb.mustReloadKeyboard
) return;
// Check if current keyboard matches requested keyboard, but not stub
if(keymanweb._ActiveKeyboard && (keymanweb._ActiveKeyboard['KI'] == PInternalName))
{
// If so, simply update the active stub
for(Ln=0; Ln 0) k = k0;
if(k)
{
if(typeof(k['KLC']) != 'undefined') lg = k['KLC'];
else if(typeof(k['LanguageCode']) != 'undefined') lg = k['LanguageCode'];
}
return ((lg == 'cmn') || (lg == 'jpn') || (lg == 'kor'));
}
keymanweb.isCJK = keymanweb['isCJK']; // I3363 (Build 301)
/**
* Allow to change active keyboard by (internal) keyboard name
*
* @param {string} PInternalName Internal name
* @param {string} PLgCode Language code
*/
keymanweb['setActiveKeyboard'] = function(PInternalName,PLgCode)
{
//TODO: This does not make sense: the callbacks should be in _SetActiveKeyboard, not here,
// since this is always called FROM the UI, which should not need notification.
// If UI callbacks are needed at all, they should be within _SetActiveKeyboard
keymanweb.doBeforeKeyboardChange(PInternalName,PLgCode);
keymanweb._SetActiveKeyboard(PInternalName,PLgCode,true);
if(keymanweb._LastActiveElement != null) keymanweb._FocusLastActiveElement();
keymanweb.doKeyboardChange(PInternalName,PLgCode);
}
/**
* Function getActiveKeyboard
* Scope Public
* @return {string} Name of active keyboard
* Description Return internal name of currently active keyboard
*/
keymanweb['getActiveKeyboard'] = function()
{
if(keymanweb._ActiveKeyboard == null) return '';
return keymanweb._ActiveKeyboard['KI'];
}
/**
* Function getActiveLanguage
* Scope Public
* @return {string} language code
* Description Return language code for currently selected language
*/
keymanweb['getActiveLanguage'] = function()
{
if(keymanweb._ActiveStub == null) return '';
return keymanweb._ActiveStub['KLC'];
}
//TODO: find all references to next three routines and disambiguate!!
/**
* Get keyboard meta data for the selected keyboard and language
*
* @param {string} PInternalName Internal name of keyboard
* @param {string=} PlgCode language code
* @return {Object} Details of named keyboard
*
**/
keymanweb['getKeyboard'] = function(PInternalName,PlgCode)
{
var Ln, Lrn;
for(Ln=0; Ln 0 && typeof(arg)=='object' && arg != null)
{
for(var p in opt)
{
if(arg.hasOwnProperty(p)) opt[p] = arg[p];
}
}
// Get default root path and device options
if(opt['root'] == '') opt['root'] = keymanweb.rootPath;
// Use root-relative paths for resources and keyboards unless set by page source
if(opt['resources'] == '') opt['resources']=opt['root'];
if(opt['keyboards'] == '') opt['keyboards']=opt['root']+'kbd/';
if(opt['fonts'] == '') opt['fonts']=opt['root']+'font/';
*/
// Set default device options
keymanweb.setDefaultDeviceOptions(opt);
// Only do remainder of initialization once!
if(keymanweb['initialized']) return;
// Do not initialize until the document has been fully loaded
if(document.readyState !== 'complete')
{
window.setTimeout(function(){keymanweb.init(arg);},50);
return;
}
keymanweb._MasterDocument = window.document;
// Initialize and protect input elements for touch-screen devices (but never for apps)
// NB: now set disabled=true rather than readonly, since readonly does not always
// prevent element from getting focus, e.g. within a LABEL element.
// c.f. http://kreotekdev.wordpress.com/2007/11/08/disabled-vs-readonly-form-fields/
if(device.touchable)
{
eTextArea=document.getElementsByTagName("textarea");
eInput=document.getElementsByTagName("input");
for(i=0; i 0)
{
keymanweb.hideOskWhileScrolling=function(e)
{
if(typeof(osk._Box) == 'undefined') return;
if(typeof(osk._Box.style) == 'undefined') return;
// The following tests are needed to prevent the OSK from being hidden during normal input!
p=e.target.parentNode;
if(typeof(p) != 'undefined' && p != null)
{
if(p.className.indexOf('keymanweb-input') >= 0) return;
if(p.className.indexOf('kmw-key-') >= 0) return;
if(typeof(p.parentNode) != 'undefined')
{
p=p.parentNode;
if(p.className.indexOf('keymanweb-input') >= 0) return;
if(p.className.indexOf('kmw-key-') >= 0) return;
}
}
osk.hideNow();
}
document.body.addEventListener('touchstart',keymanweb.hideOskWhileScrolling,false);
}
else
{
keymanweb.conditionallyHideOsk = function()
{
if(keymanweb.hideOnRelease) osk.hideNow();
keymanweb.hideOnRelease=false;
}
keymanweb.hideOskIfOnBody = function(e)
{
keymanweb.touchY=e.touches[0].screenY;
keymanweb.hideOnRelease=true;
}
keymanweb.cancelHideIfScrolling = function(e)
{
var y=e.touches[0].screenY,y0=keymanweb.touchY;
if(y-y0 > 5 || y0-y < 5) keymanweb.hideOnRelease = false;
}
document.body.addEventListener('touchstart',keymanweb.hideOskIfOnBody,false);
document.body.addEventListener('touchmove',keymanweb.cancelHideIfScrolling,false);
document.body.addEventListener('touchend',keymanweb.conditionallyHideOsk,false);
}
}
if(osk.shim)
document.body.appendChild(osk.shim); // I1476 - Handle SELECT overlapping
//document.body.appendChild(keymanweb._StyleBlock);
// IE: call _SelectionChange when the user changes the selection
if(document.selection && !keymanweb.legacy)
util.attachDOMEvent(document, 'selectionchange', keymanweb._SelectionChange);
// Add event listeners and attach manually-positioned KMW objects
if(keymanweb.options['attachType'] != 'manual') // I1961
{
if(document.attachEvent)
document.attachEvent('onfocusin', keymanweb._IEFocusIn);
else if(document.addEventListener)
document.addEventListener('DOMNodeInserted', keymanweb._DOMNodeInserted, true);
}
// Restore and reload the currently selected keyboard
keymanweb.restoreCurrentKeyboard();
// Set exposed initialization flag to 2 to indicate deferred initialization also complete
keymanweb['initialized']=2;
}
// Create an ordered list of all text and search input elements and textarea elements
// except any tagged with class 'kmw-disabled'
// TODO: email and url types should perhaps use default keyboard only
keymanweb.listInputs = function()
{
var i,eList=[],
t1=document.getElementsByTagName('INPUT'),
t2=document.getElementsByTagName('TEXTAREA');
for(i=0; i} selection start
* Description Get input selection for all(?) browsers, per Tim Down
* http://stackoverflow.com/questions/3053542/how-to-get-the-start-and-end-points-of-selection-in-text-area/3053640#3053640
* But only works for input fields, not for content editable fields!!!
**/
keymanweb.getInputSelection = function(el)
{
var start = 0, end = 0, normalizedValue = '', range, textInputRange, len = 0, endRange;
if(typeof el.selectionStart == "number" && typeof el.selectionEnd == "number") {
start = el.selectionStart; end = el.selectionEnd;
} else {
range = document.selection.createRange();
if(range && range.parentElement() == el) {
len = el.value.length;
normalizedValue = el.value.replace(/\r\n/g, "\n");
// Create a working TextRange that lives only in the input
textInputRange = el.createTextRange();
textInputRange.moveToBookmark(range.getBookmark());
// Check if the start and end of the selection are at the very end of the input,
// since moveStart/moveEnd doesn't return what we want in those cases
endRange = el.createTextRange();
endRange.collapse(false);
if(textInputRange.compareEndPoints("StartToEnd", endRange) > -1) {
start = end = len;
} else {
start = -textInputRange.moveStart("character", -len);
start += normalizedValue.slice(0, start).split("\n").length - 1;
if(textInputRange.compareEndPoints("EndToEnd", endRange) > -1) {
end = len;
} else {
end = -textInputRange.moveEnd("character", -len);
end += normalizedValue.slice(0, end).split("\n").length - 1;
}
}
}
}
return {start: start, end: end};
}
// *** I3319 Supplementary Plane modifications - end new code
// I3318 - deadkey changes START
/**
* Function _DeadkeyResetMatched
* Scope Private
* Description Clear all matched deadkey flags
*/
keymanweb._DeadkeyResetMatched = function()
{
var Li, _Dk = keymanweb._DeadKeys;
for(Li = 0; Li < _Dk.length; Li++) _Dk[Li].matched = 0;
}
/**
* Function _DeadkeyDeleteMatched
* Scope Private
* Description Delete matched deadkeys from context
*/
keymanweb._DeadkeyDeleteMatched = function()
{
var Li, _Dk = keymanweb._DeadKeys;
for(Li = 0; Li < _Dk.length; Li++) if(_Dk[Li].matched) _Dk.splice(Li,1);
}
/**
* Function _DeadkeyAdjustPos
* Scope Private
* @param {number} Lstart start position in context
* @param {number} Ldelta characters to adjust by
* Description Adjust saved positions of deadkeys in context
*/
keymanweb._DeadkeyAdjustPos = function(Lstart, Ldelta)
{
var Li, _Dk = keymanweb._DeadKeys;
for(Li = 0; Li < _Dk.length; Li++) if(_Dk[Li].p > Lstart) _Dk[Li].p += Ldelta;
}
// I3318 - deadkey changes END
/**
* Set target element text direction (LTR or RTL), but only if the element is empty
*
* If the element base directionality is changed after it contains content, unless all the text
* has the same directionality, text runs will be re-ordered which is confusing and causes
* incorrect caret positioning
*
* @param {Object} Ptarg Target element
*/
keymanweb._SetTargDir = function(Ptarg)
{
var elDir=((keymanweb._ActiveKeyboard != null) && (keymanweb._ActiveKeyboard['KRTL'])) ? 'rtl' : 'ltr';
if(Ptarg)
{
if(device.touchable)
{
if(Ptarg.textContent.length == 0)
{
Ptarg.base.dir=Ptarg.dir=elDir;
keymanweb.setTextCaret(Ptarg,10000);
}
}
else
{
if(Ptarg.value.length == 0) Ptarg.dir=elDir;
}
}
}
//TODO: check use of following function, what value should it return??
/**
* Callback used by IE/non-IE browsers to attach KMW objects to elements
**/
keymanweb._IEFocusIn = function() // I1596 - attach to controls dynamically
{
var e = keymanweb._GetEventObject(null); // I2404 - Support for IE events in IFRAMEs
if(!e) return;
var Pelem=e.srcElement;
if(Pelem != null && !keymanweb._IsAttached(Pelem))
if((Pelem.tagName.toLowerCase() == 'input' && Pelem.type.toLowerCase() == 'text') || Pelem.tagName.toLowerCase() == 'textarea' || Pelem.tagName.toLowerCase() == 'iframe')
keymanweb.attachToControl(Pelem);
}
/**
* Callback used by non-IE browsers to attach KMW objects to elements
* Actions to execute if new elements are added to page
*
* @param {Event} e event object
**/
keymanweb._DOMNodeInserted = function(e)
{
var Pelem=e.target;
if(Pelem != null && Pelem.nodeType == 1) // I1703 - crash when nodeType != 1
{ //TODO: Should this really be used for touch devices???
keymanweb._AttachToControls(Pelem); // I1961, I1976
}
}
/**
* Reset OSK shift states when entering or exiting the active element
**/
keymanweb._ResetVKShift = function()
{
if(!keymanweb._IsActivatingKeymanWebUI)
{
if(osk._UpdateVKShift) osk._UpdateVKShift(null,15,0); //this should be enabled !!!!! TODO
}
}
/**
* Function addHotKey
* Scope Public
* @param {number} keyCode
* @param {number} shiftState
* @param {function(Object)} handler
* Description Add hot key handler to array of document-level hotkeys triggered by key up event
*/
keymanweb['addHotKey'] = keymanweb.addHotKey = function(keyCode,shiftState,handler)
{
// Test if existing handler for this code and replace it if so
for(var i=0; i 0) return stubs[0]['KI']+':'+stubs[0]['KLC'];
// Or US English if no stubs loaded (should never happen)
return 'Keyboard_us:eng';
}
util.attachDOMEvent(window, 'focus', keymanweb._ResetVKShift,false); // I775
util.attachDOMEvent(window, 'blur', keymanweb._ResetVKShift,false); // I775
// Initialize supplementary plane string extensions
String.kmwEnableSupplementaryPlane(false);
})();