spiegel-keyman/source/kmwuitoggle.js
2014-11-05 09:41:26 +11:00

743 lines
24 KiB
JavaScript

/***
KeymanWeb 2.0
Copyright 2014 Tavultesoft Pty Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
***/
/********************************/
/* */
/* Toggle User Interface Code */
/* */
/********************************/
/**
* Do not enclose in an anonymous function, as the compiler may create
* global scope variables to replace true, false, null, which may collide
* with other variables.
* Instead, use the --output-wrapper command during optimization, which will
* add the anonymous function to enclose all code, including those optimized
* variables which would otherwise have global scope.
**/
try {
// Declare KeymanWeb, OnScreen Keyboard and Util objects
var keymanweb=window['tavultesoft']['keymanweb'],osk=keymanweb['osk'],util=keymanweb['util'];
var dbg=keymanweb['debug'];
// Disable UI for touch devices
if(util['isTouchDevice']()) throw '';
// Initialize user interface common variables
var ui=keymanweb['ui'] = {
name: 'toggle',
initialized: false,
shim: null,
controller: null,
oskButton: null,
kbdButton: null,
controllerHovered: false,
keyboards: [],
lastActiveKeyboard: -1,
selectedMenuItem: null,
updateList: true,
updateTimer: null
}
/**
* Update the KeymanWeb user interface when an input element is focused or blurred
*
* @param {Object} someElement focused element
* @param {(boolean|number)} focusing true if focusing
*/
ui.doFocus = function(someElement,focusing)
{
// This callback must be ignored until UI is initialized, or for touch devices (which can never initialize the UI)
if(!ui.initialized) return;
if(window.event) someElement=window.event.srcElement;
if(focusing)
{
ui.controller.style.display = 'block';
}
else
{
if(!(keymanweb['getUIState']()['activationPending']) && !ui.controllerHovered)
{
ui.controller.style.display = 'none';
}
}
/* I2406 - Find an appropriate position for the controller */
var x, y, w, h, p;
p = util['getAbsolute'](someElement); x = p['x']; y=p['y'];
if(someElement.parentWindow && someElement.parentWindow.frameElement)
{
w = someElement.parentWindow.frameElement.clientWidth;
h = someElement.parentWindow.frameElement.clientHeight;
}
else if(someElement.defaultView && someElement.defaultView.frameElement)
{
w = someElement.defaultView.frameElement.clientWidth;
h = someElement.defaultView.frameElement.clientHeight;
}
else
{
w = someElement.offsetWidth;
h = someElement.offsetHeight;
}
if(x + w > (window.clientWidth?window.clientWidth:window.innerWidth) + document.documentElement.scrollLeft - ui.controller.offsetWidth - 1)
y += h;
else
{
x += w + 2;
//y += h - ui.controller.offsetHeight - 1; // ui.controller.offsetheight is returned as its prior value, which results in incorrect UI positioning
// so better to set the offset absolutely JMD 11/2/11
y += h - 29; //prevent UI being positioned *above* the top of the element
}
if(isNaN(x) || isNaN(y)) return;
ui.controller.style.left = x + 'px';
ui.controller.style.top = y + 'px';
}
keymanweb['addEventListener']('controlfocused',function(params){ui.doFocus(params.target,true);});
keymanweb['addEventListener']('controlblurred',function(params){ui.doFocus(params.target,false);});
osk['addEventListener']('show',
function(oskPosition)
{
// Ensure that the ui.controller is visible if help is displayed
ui.controller.style.display = 'block';
ui.oskButton._setSelected(true);
/* The following is probably not needed for KMW2, since OSK position is set by KMW, not by the UI
//TODO: This may not be correct - may need to refer to userLocated argument, not function???
// Check if OSK position is user-defined, return if so
if (osk['userLocated']()) return oskPosition;
// Otherwise, return the position with respect to the focussed element
var someElement = keymanweb['getLastActiveElement'](), x, y, w, h, p;
if(someElement != null)
{
p = util['getAbsolute'](someElement); x = p['x']; y = p['y'];
if(someElement.parentWindow && someElement.parentWindow.frameElement)
{
w = someElement.parentWindow.frameElement.clientWidth;
h = someElement.parentWindow.frameElement.clientHeight;
}
else if(someElement.defaultView && someElement.defaultView.frameElement)
{
w = someElement.defaultView.frameElement.clientWidth;
h = someElement.defaultView.frameElement.clientHeight;
}
else
{
w = someElement.offsetWidth;
h = someElement.offsetHeight;
}
//TODO: check the logic of this - it doesn't look right!!! Signs on top, height??
var r = osk['getRect']();
x += 32;
if(y + h + (r.height - r.top) > (window.clientHeight?window.clientHeight:window.innerHeight) + document.documentElement.scrollTop &&
y - (r.height - r.top) >= document.documentElement.scrollTop)
{
y -= (r.height - r.top);
}
else
y += h;
}
else
{
x = ui.controller.offsetLeft;
y = ui.controller.offsetTop + ui.controller.offsetHeight;
}
oskPosition['x'] = x;
oskPosition['y'] = y;
*/
return oskPosition;
});
osk['addEventListener']('hide',
function(byUser)
{
if(byUser['HiddenByUser']) ui.oskButton._setSelected(false);
});
/**
* Toggle the on screen keyboard display - KMW button control event
**/
ui.switchOsk = function()
{
// Check that user control of OSK is allowed
if((keymanweb['getActiveKeyboard']() == '') || keymanweb['isCJK']() ) return;
osk['show'](!osk['isEnabled']());
}
/**
* Toggle a single keyboard on or off - KMW button control event
**/
ui.switchSingleKbd = function()
{
var _v = (keymanweb['getActiveKeyboard']() == ''),nLastKbd=0,kbdName='',lgCode='';
if(_v)
{
if(ui.keyboards.length == 0) return;
if(ui.lastActiveKeyboard < ui.keyboards.length && ui.lastActiveKeyboard >= 0) nLastKbd = ui.lastActiveKeyboard;
kbdName = ui.keyboards[nLastKbd]._InternalName;
lgCode = ui.keyboards[nLastKbd]._LanguageCode;
keymanweb['setActiveKeyboard'](kbdName,lgCode);
ui.lastActiveKeyboard = nLastKbd;
}
else
{
keymanweb['setActiveKeyboard']('');
}
if(ui.kbdButton) ui.kbdButton._setSelected(_v);
}
/**
* Switch to the next keyboard in the list - KMW button control event
**/
ui.switchNextKbd = function()
{
var _v = (keymanweb['getActiveKeyboard']() == ''),kbdName='',lgCode='';
if(_v)
{
if(ui.keyboards.length == 0) return;
kbdName = ui.keyboards[0]._InternalName;
lgCode = ui.keyboards[0]._LanguageCode;
keymanweb['setActiveKeyboard'](kbdName,lgCode);
ui.lastActiveKeyboard = 0;
}
else
{
if(ui.lastActiveKeyboard == ui.keyboards.length-1)
{
keymanweb['setActiveKeyboard']('');
_v = false;
}
else
{
kbdName = ui.keyboards[++ui.lastActiveKeyboard]._InternalName;
lgCode = ui.keyboards[ui.lastActiveKeyboard]._LanguageCode;
keymanweb['setActiveKeyboard'](kbdName,lgCode);
_v = true;
}
}
if(ui.kbdButton) ui.kbdButton._setSelected(_v);
}
/**
* Create a button object for KeymanWeb UI buttons
*
* @constructor
* @param {string} _src
* @param {string} _caption
* @param {boolean} _selected
* @return {Object}
**/
ui.button = function(_src, _caption, _selected)
{
this._onclick = null;
this._onmouseover = null;
this._onmouseout = null;
this._elem = null;
this._down = false;
this._over = false;
this._selected = _selected;
/*public*/ this.getElem = function()
{
return this._owningObject._elem;
};
/*private*/ this.__updatestyle = function()
{
var ss=this._owningObject._elem.style;
if(this._owningObject._over)
{
ss.margin = '0px';
if(this._owningObject._selected) {
ss.border = 'solid 1px #ad4a28';
ss.background = '#dfb4b4';
}
else {
ss.border = 'solid 1px #dfb4b4';
ss.background = '#f3e5de';
}
}
else if(this._owningObject._selected)
{
ss.background = '#f3e5de';
ss.margin = '0px';
ss.border = 'solid 1px #ad4a28';
}
else
{
ss.background = 'none';
ss.margin = '1px';
ss.border = 'none';
}
};
/*private*/ this.__mouseover = function()
{
ui.controllerHovered = true;
this._owningObject._over = true;
if(this._owningObject._onmouseover != null) this._owningObject._onmouseover();
this._owningObject.__updatestyle();
};
/*private*/ this.__mouseout = function()
{
ui.controllerHovered = false;
this._owningObject._over = false;
if(this._owningObject._onmouseout != null) this._owningObject._onmouseout();
this._owningObject.__updatestyle();
};
/*private*/ this.__click = function()
{
keymanweb['activatingUI'](false); // Clear activating UI flag once click is acknowledged
if(this._owningObject._onclick != null)
return this._owningObject._onclick();
return false;
};
/*private*/ this.__mousedown = function()
{
keymanweb['activatingUI'](true); // Set activating UI flag (to manage focus/blur) on any UI mouse down event
this._owningObject._down = true;
this._owningObject.__updatestyle();
return false;
};
/*private*/ this.__mouseup = function()
{
this._owningObject._down = false;
this._owningObject.__updatestyle();
};
/*public*/ this._setSelected = function(_value)
{
keymanweb['activatingUI'](false); // Always clear activating UI flag after selecting UI
this._owningObject._selected = _value;
this._owningObject.__updatestyle();
};
/*public*/ this._getSelected = function()
{
return this._owningObject._selected;
};
/*public*/ this._getOver = function()
{
return this._owningObject._over;
};
/*public*/ this._getDown = function()
{
return this._owningObject._down;
};
this._owningObject = this; // simplifies meaning of 'this'
var imgPath=util['getOption']('resources') + 'ui/toggle/';
var _elemImg = util['createElement']('img');
this._elem = util['createElement']('div');
this._elem._owningObject = this;
_elemImg.style.display = 'block';
_elemImg.src = imgPath + _src;
_elemImg.id = 'KMW_Controller_Img';
this._elem.style.margin = '0px'; //display = 'inline';
this._elem.style.width = '24px';
this._elem.style.height = '24px';
this._elem.style.zIndex = '10002';
this._elem.style.lineHeight = '100%';
this._elem.style.styleFloat = this._elem.style.cssFloat = 'left';
_elemImg.title = _caption;
_elemImg.alt = _caption;
this._elem.appendChild(_elemImg);
this._elem.onmouseover = this.__mouseover;
this._elem.onmouseout = this.__mouseout;
this._elem.onmousedown = this.__mousedown;
this._elem.onmouseup = this.__mouseup;
_elemImg._owningObject = this;
_elemImg.onclick = this.__click;
this.__updatestyle();
return this;
};
/**
* Function Initialize
* Scope Private
* Description Initialize Toggle User Interface
**/
ui['initialize'] = ui.Initialize = function()
{
//Never initialize before KMW!
if(!keymanweb['initialized'] || util['isTouchDevice']()) return;
if(!ui.initialized) // I2403 - Allow toggle design to be loaded twice
{
ui.controller = util['createElement']('div');
}
else
ui.controller.innerHTML = ''; // I2403 - Allow toggle design to be loaded twice
var imgPath = util['getOption']('resources')+'ui/toggle/';
ui.controller.style.background = 'url('+imgPath+'kmwcontroller2x.gif)';
ui.controller.style.padding = '1px 2px';
// Create keyboard list and OSK control buttones, and set initial styles
var v1=util['loadCookie']('KeymanWeb_Keyboard'),kbdEnabledOnLoad=false;
if(typeof(v1['current'])!='undefined') kbdEnabledOnLoad = (v1['current'].indexOf('---') < 0);
ui.kbdButton = new ui.button('kmw_logo_16.gif', 'Use Web Keyboard', kbdEnabledOnLoad);
ui.controller.appendChild(ui.kbdButton.getElem());
var v2 = util['loadCookie']('KeymanWeb_OnScreenKeyboard'),oskEnabledOnLoad=true;
if(typeof(v2['visible'])!='undefined') oskEnabledOnLoad=(v2['visible'] == 1);
// Add keyboard icon
ui.oskButton = new ui.button('kmw_osk_16.gif','Show On Screen Keyboard',oskEnabledOnLoad);
ui.oskButton._onclick = ui.switchOsk;
ui.controller.appendChild(ui.oskButton.getElem());
// Hide controller unless already initialized
if(!ui.initialized) ui.controller.style.display = 'none';
ui.controller.style.zIndex = '10001';
ui.controller.style.position = 'absolute';
// The following three lines prevent the UI from being positioned incorrectly when the page is resized,
// but don't fix the problem completely, as the kbd icon still moves. probably need to insert a DIV
// between the button and the container, and make that DIV fixed height and overflow:hidden
//ui.controller.style.maxHeight = '26px';
//ui.oskButton.getElem().style.position = 'relative';
//ui.oskButton.getElem().style.overflow = 'hidden';
if(!ui.initialized) // I2403 - Allow toggle design to be loaded more than once if necessary
document.body.appendChild(ui.controller);
// Set initialized true
ui.initialized = true; // I2403 - Allow toggle design to be loaded more than once if needed
// Then update the keyboard list if keyboards already loaded (i.e. in page script)
ui.updateKeyboardList();
}
/**
* Function updateKeyboardList
* Scope Private
* Description Rebuild the UI and keyboard list
**/
ui.updateKeyboardList=function()
{
if(!(keymanweb['initialized'] || ui.initialized)) return; //TODO: may want to restart the timer??
ui.updateList = false;
var _kbds=keymanweb['getKeyboards'](),imgPath=util['getOption']('resources') +'ui/toggle/';
// Check the number of installed keyboards to determine whether or not we will have a dropdown
if(_kbds.length > 1)
{
// Multiple keyboards
var _kmw_ctrl_img=document.getElementById('KMW_Controller_Img')
_kmw_ctrl_img.src = imgPath+'kmw_logo_16_down.gif';
_kmw_ctrl_img.style.width = '100%';
ui.controller.style.background = 'url('+imgPath+'kmwcontroller2x.gif)';
ui.kbdButton.getElem().id = 'kmwico';
ui.kbdButton.getElem().style.width = '36px';
ui.kbdButton._onmouseover = function()
{
ui.keyboardMenu.className="sfhover";
if(ui.shim)
{
var ss=ui.shim.style;
ss.left = ui.keyboardMenu.style.left;
ss.top = ui.keyboardMenu.style.top;
ss.width = ui.keyboardMenu.offsetWidth+"px";
ss.height = ui.keyboardMenu.offsetHeight+"px";
ss.zIndex = ui.keyboardMenu.style.zIndex-1;
ss.display = 'block';
}
};
ui.kbdButton._onmouseout = function()
{
ui.keyboardMenu.className="sfunhover";
if(ui.shim) ui.shim.style.display = 'none';
};
ui.kbdButton._onclick = null;
ui.createMenu();
}
// Single keyboard
else if(_kbds.length == 1)
{
var Lki=_kbds[0]['InternalName'];
var Lklc=_kbds[0]['LanguageCode'];
ui.controller.style.background = 'url('+imgPath+'kmwcontroller2.gif)';
ui.keyboards.push({_InternalName: Lki, _LanguageCode: Lklc, _Index: 0});
ui.kbdButton._onclick = ui.switchSingleKbd;
// Must remove menu if keyboards have been removed leaving only a single keyboard
if(typeof(ui.keyboardMenu) != 'undefined') delete ui.keyboardMenu;
}
// Highlight the last active keyboard
var sk=keymanweb['getSavedKeyboard']().split(':');
ui.updateMenu(sk[0],sk[1]);
}
/**
* Keyboard registration event handler
*
* Set a timer to update the UI keyboard list on timeout after each keyboard is registered,
* thus updating only once when only if multiple keyboards are registered together
*/
keymanweb['addEventListener']('keyboardregistered',
function(p)
{
ui.updateList = true;
if(ui.updateTimer) clearTimeout(ui.updateTimer);
ui.updateTimer = setTimeout(ui.updateKeyboardList,200);
});
/**
* Keyboard change event handler
*
* Update menu selection and control OSK display appropriately
*/
keymanweb['addEventListener']('keyboardchange',
function(p)
{
ui.updateMenu(p['internalName'],p['languageCode']);
});
/* ----------------------------------------
Drop down menu
---------------------------------------- */
// var _SelectedMenuItem;
/**
* Function selecKbd
* Scope Private
* @param {number} _kbd
* Description Select a keyboard from the drop down menu
**/
ui.selectKbd = function(_kbd)
{
var _name,_lgCode,_index;
if(_kbd < 0)
{
_name = ''; _lgCode='';_index = '';
}
else
{
_name = ui.keyboards[_kbd]._InternalName; _lgCode = ui.keyboards[_kbd]._LanguageCode; _index = ui.keyboards[_kbd]._Index;
}
keymanweb['setActiveKeyboard'](_name,_lgCode);
keymanweb['focusLastActiveElement']();
ui.kbdButton._setSelected(_name != '');
if(_kbd >= 0) ui.lastActiveKeyboard = _kbd;
return false;
};
/**
* Function updateMenu
* Scope Private
* @param {string} kbdName
* @param {?string=} lgCode
* Description Updates the menu selection when a change is required
**/
ui.updateMenu = function(kbdName,lgCode)
{
var i,_k=document.getElementById('KMWSel_$');
for(i=0; i<ui.keyboards.length; i++)
{
if(ui.keyboards[i]._InternalName == kbdName && ui.keyboards[i]._LanguageCode == lgCode)
_k=document.getElementById('KMWSel_'+ui.keyboards[i]._InternalName+'$'+ui.keyboards[i]._Index);
}
if(_k)
{
if(ui.selectedMenuItem != null) ui.selectedMenuItem.className='';
_k.className='selected'; ui.selectedMenuItem=_k;
}
// Hide the OSK button for CJK keyboards (or non-mapped)
if(lgCode=='cmn' || lgCode=='jpn' || lgCode=='kor')
{
ui.oskButton.getElem().style.display='none';
//osk['show'](true);
}
else if(kbdName == '')
{
ui.oskButton.getElem().style.display='none';
}
else
{
ui.oskButton.getElem().style.display='block';
//osk['show'](osk['isEnabled']());
}
}
util['addStyleSheet'](
"#KeymanWeb_KbdList {"+
"display: block;"+
"position: absolute;"+
"width: auto;"+ ((util['getIEVersion']() < 8) ? "200px;" : "auto;")+
"line-height: 100%;"+
"margin: 0;"+
"clear: both;"+
"float: none;"+
"top: auto;"+
"border: solid 2px #ad4a28;"+
"-moz-border-radius: 4px;"+
"-webkit-border-radius: 4px;"+
"border-radius: 4px;"+
"box-shadow: 4px 4px 2px rgba(136,136,136,.5);"+
"-webkit-box-shadow: 4px 4px 2px rgba(136,136,136,.5);"+
"-moz-box-shadow: 4px 4px 2px rgba(136,136,136,.5);"+
((util['getIEVersion']() < 99) ? "filter:progid:DXImageTransform.Microsoft.DropShadow(OffX=4,OffY=4,Color=#80646464);" : "")+
"list-style: none;"+
"padding: 0;"+
"background: white;"+
"max-height: 300px;"+
"overflow-y: scroll;"+
"overflow-x: hidden;"+
"white-space: nowrap;"+
"z-index: 10001; /* above the osk */"+
"}"+
((util['getIEVersion']() < 7) ?
"* html #KeymanWeb_KbdList {height: expression(this.scrollHeight > 299 ? '300px' : 'auto');}" : "")+
".sfunhover#KeymanWeb_KbdList {"+
"display: none; left: -999px;"+
"}"+
".sfhover#KeymanWeb_KbdList {"+
"display: block;"+
"left: auto;"+
"}"+
"#KeymanWeb_KbdList li {"+
"float: none;"+
"width: auto;"+
"padding: 0;"+
"margin: 0;"+
"text-align: left;"+
"}"+
"#KeymanWeb_KbdList li a {"+
"display: block; "+
"padding: 2px 4px;"+
"color: #404040;"+
"font-family: Tahoma,Verdana,Arial,sans-serif;"+
"font-size: 8pt;"+
"text-decoration: none;"+
"}"+
"#KeymanWeb_KbdList li a.selected {"+
"font-weight: bold;"+
"color: black;"+
"}"+
"#KeymanWeb_KbdList li a:hover {"+
"color: white;"+
"background-color: #ad4a28;"+
"text-decoration: underline;"+
"}");
/**
* Function createMenu
* Scope Private
* Description Create the drop down menu and populate with loaded KeymanWeb keyboards
**/
ui.createMenu = function()
{
if(typeof(ui.keyboardMenu) == 'undefined') // I2403 - Allow toggle design to be loaded twice
{
if (window.createPopup && !('XmlHttpRequest' in window))
{
ui.shim = util['createElement']('iframe');
ui.shim.src = '';
ui.shim.style.display = 'none';
ui.shim.style.position = 'absolute';
ui.shim.style.filter = 'progidXImageTransform.Microsoft.Alpha(style=0,opacity=0)';
ui.shim.frameBorder = '0';
ui.shim.scrolling = 'no';
ui.kbdButton.getElem().appendChild(ui.shim);
}
ui.keyboardMenu=util['createElement']('ul');
ui.keyboardMenu.id='KeymanWeb_KbdList';
ui.keyboardMenu.className='sfunhover';
}
else ui.keyboardMenu.innerHTML = ''; // I2403 - Allow toggle design to be loaded twice
var _li=util['createElement']('li');
var _a=util['createElement']('a');
_a.innerHTML='English';
_a.href="#";
_a.onclick = function() { return ui.selectKbd(-1); };
_a.id='KMWSel_$';
_a.className='selected';
_li.appendChild(_a);
ui.selectedMenuItem=_a;
ui.keyboardMenu.appendChild(_li);
var _kbds=keymanweb['getKeyboards'](), _added=[];
ui.keyboards=[];
for(var _kbd = 0; _kbd < _kbds.length; _kbd++)
{
var _li1=util['createElement']('li');
var _a1=util['createElement']('a');
_a1.innerHTML=_kbds[_kbd]['LanguageName'] + ' - ' + _kbds[_kbd]['Name'];
if(!_added[_kbds[_kbd]['InternalName']]) _added[_kbds[_kbd]['InternalName']]=0;
_added[_kbds[_kbd]['InternalName']]++;
var _n=_added[_kbds[_kbd]['InternalName']];
ui.keyboards.push({_InternalName: _kbds[_kbd]['InternalName'], _LanguageCode:_kbds[_kbd]['LanguageCode'], _Index: _n});
_a1.href="#";
_a1.onclick = (function(x) { return function() { return ui.selectKbd(x); } })(ui.keyboards.length-1);
_a1.id='KMWSel_'+_kbds[_kbd]['InternalName']+'$'+_n;
_li1.appendChild(_a1);
ui.keyboardMenu.appendChild(_li1);
}
//if(!ui.initialized) // I2403 - Allow toggle design to be loaded twice
if(ui.keyboardMenu.parentNode != ui.kbdButton.getElem()) ui.kbdButton.getElem().appendChild(ui.keyboardMenu);
};
keymanweb['addHotKey'](191,0x20,ui.switchSingleKbd);
keymanweb['addHotKey'](191,0x30,ui.switchNextKbd);
keymanweb['addHotKey'](191,0x40,ui.switchOsk);
// Initialize after KMW is fully initialized
keymanweb['addEventListener']('loaduserinterface',ui.Initialize);
// but also execute here, for asynchronous UI script loading (occurring after KMW initialization)
ui.Initialize();
} catch(ex){}