spiegel-keyman/web/source/kmwuifloat.js

532 lines
No EOL
18 KiB
JavaScript

/***
KeymanWeb 10.0
Copyright 2017 SIL International
***/
// If a UI module has been loaded, we can rely on the publically-published 'name' property
// having been set as a way to short-out a UI reload. Its parent object always exists by
// this point in the build process.
if(!window['tavultesoft']['keymanweb']['ui']['name']) {
/********************************/
/* */
/* Floating User Interface */
/* */
/********************************/
/**
* Do not enclose in an anonymous function, as the compiler may create
* global scope variables to replace true, false, null, which can 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'];
var util=keymanweb['util'];
var osk=keymanweb['osk'];
var dbg=keymanweb['debug'];
// Disable UI for touch devices
if(util['isTouchDevice']()) throw '';
// User interface global and local variables
keymanweb['ui'] = {};
var ui=keymanweb['ui'];
ui['name'] = 'float';
ui.KeyboardSelector = null;
ui.outerDiv = null; // replaces DivKeymanWeb
ui.innerDiv = null; // replaces Lkdiv
ui.oskButton = null; // toggles OSK on or off
ui.kbdIcon = null; // keyboard icon within OSK button
ui.selecting = false; // control focus behaviour during selection
ui.updateList = true; // control keyboard list updating
ui.updateTimer = null; // prevent unnecessary list refreshing
ui.floatRight = false; // align left by default
ui.initialized = false; // initialization flag
/**
* Display or hide the OSK from the OSK icon link
*/
ui.toggleOSK = function()
{
keymanweb['focusLastActiveElement']();
if(osk['show'])
{
if(osk['isEnabled']()) osk['hide'](); else osk['show'](true);
}
if(window.event) window.event.returnValue=false;
return false;
}
/**
* Function Initialize
* Scope Private
* Description UI Initialization
**/
ui.Initialize = function()
{
// Must always initialize after keymanWeb itself, otherwise options are undefined
if(!keymanweb['initialized'])
{
window.setTimeout(ui.Initialize,50); return;
}
if(ui.initialized || util['isTouchDevice']()) return;
var imgPath=util['getOption']('resources')+"ui/float/";
ui.outerDiv = util['createElement']('DIV'); // Container for UI (visible when KeymanWeb is active)
ui.innerDiv = util['createElement']('DIV'); // inner div for UI
ui.kbdIcon = util['createElement']('IMG');
ui.outerDiv.innerHTML = "<a href='http://keyman.com/web/' target='KeymanWebHelp'>"
+ "<img src='"+imgPath+"kmicon.gif' border='0' style='padding: 0px 2px 0 1px; margin:0px;' title='KeymanWeb' alt='KeymanWeb' /></a>"; /* I2081 */
var s=ui.outerDiv.style;
s.backgroundColor='white'; s.border='solid 1px black'; s.position='absolute'; s.height='18px';
s.font='bold 8pt sans-serif'; s.display='none'; s.textAlign='left';s.overflow='hidden';
util['attachDOMEvent'](ui.outerDiv,'mousedown',ui._SelectorMouseDown);
util['attachDOMEvent'](ui.outerDiv,'mouseover',ui._SelectorMouseOver);
util['attachDOMEvent'](ui.outerDiv,'mouseout',ui._SelectorMouseOut);
// Register keymanweb events
ui.registerEvents();
ui.kbdIcon.src = imgPath+'kbdicon.gif';
ui.kbdIcon.title = 'Display visual keyboard';
// Set initial OSK button style (off by default)
ui.oskButtonState(false);
var Lhdiv = util['createElement']('DIV');
ui.oskButton = Lhdiv;
Lhdiv.onclick = ui.toggleOSK;
Lhdiv.appendChild(ui.kbdIcon);
ui.innerDiv.appendChild(Lhdiv);
ui.outerDiv.appendChild(ui.innerDiv);
document.body.appendChild(ui.outerDiv);
ui.KeyboardSelector = util['createElement']('SELECT'); // ControlSelector - KeymanWeb keyboard selector
s=ui.KeyboardSelector.style;
s.font='8pt sans-serif'; s.backgroundColor='#f3e5de'; s.border='solid 1px #7B9EBD'; s.height='16px';s.margin='1px 2px 0px 2px';
s.left='20px'; s.top='0px'; s.position='absolute'; s.maxWidth='150px';
util['attachDOMEvent'](ui.KeyboardSelector,'change',ui.SelectKeyboardChange);
util['attachDOMEvent'](ui.KeyboardSelector,'blur',ui.SelectBlur);
ui.innerDiv.appendChild(ui.KeyboardSelector); //this may need to be moved up....
// Check required interface alignment and default keyboard
var opt=util['getOption']('ui'),dfltKeyboard='English';
if(typeof(opt) == 'object')
{
if(typeof(opt['position']) == 'string' && opt['position'] == 'right')
ui.floatRight = true;
if(typeof(opt['default']) == 'string')
dfltKeyboard = opt['default'];
}
var Lopt = util['createElement']('OPTION');
Lopt.value = '-';
Lopt.innerHTML = dfltKeyboard;
ui.KeyboardSelector.appendChild(Lopt);
Lopt = null;
// This must be set before updating the keyboard list to prevent recursion!
ui.initialized = true;
// Update the keyboard list if required
ui.updateKeyboardList();
//may also want to initialize style sheet here ??
}
/**
* UI removal - resource cleanup
*/
keymanweb['addEventListener']('unloaduserinterface',
function()
{
ui.KeyboardSelector = ui.innerDiv = ui.outerDiv = ui.kbdIcon = null;
});
/**
* Update list of keyboards shown by UI
*/
ui.updateKeyboardList = function()
{
// Do nothing unless list requires updating
if(ui.updateList)
{
if(!ui.initialized) ui.Initialize();
// Remove current list (except for default element)
var i,opts=ui.KeyboardSelector.getElementsByTagName('OPTION');
for(i=opts.length; i>1; i--)
{
ui.KeyboardSelector.removeChild(opts[i-1]);
}
// Loop over installed keyboards and add to selection list
var Ln,Lopt,Lkbds=keymanweb['getKeyboards']();
for(Ln=0; Ln<Lkbds.length; Ln++)
{
Lopt = util['createElement']('OPTION');
Lopt.value = Lkbds[Ln]['InternalName']+':'+Lkbds[Ln]['LanguageCode'];;
Lopt.innerHTML = Lkbds[Ln]['Name'].replace(/\s?keyboard/i,'');
if(Lkbds[Ln]['LanguageName'])
{
var lg=Lkbds[Ln]['LanguageName'];
// Only show the main language name if variants indicated (this is tentative)
// e.g. Chinese rather than Chinese, Mandarin, which is in the keyboard name
lg = lg.split(',')[0];
if(Lkbds[Ln]['Name'].search(lg) == -1)
Lopt.innerHTML = lg+' ('+Lopt.innerHTML+')'; // I1300 - Language support
}
ui.KeyboardSelector.appendChild(Lopt);
Lopt = null;
}
}
ui.updateList = false;
// Set the menu selector to the currently saved keyboard
var sk = keymanweb['getSavedKeyboard']().split(':');
if(sk.length < 2) sk[1] = '';
ui.updateMenu(sk[0],sk[1]);
// Redisplay the UI to correct width for any new language entries
if(keymanweb['getLastActiveElement']())
{
ui.HideInterface();
ui.ShowInterface();
}
}
/**
* Function updateMenu
* Scope Private
* @param {string} kbd
* @param {string} lg
* Description Update the UI menu selection
* Separate arguments passed to allow better control of selection when a keyboard
* is listed more than once for different language codes
*/
ui.updateMenu = function(kbd,lg)
{
var i=0,value='English';
// This can be called during startup before fully initialized - ignore if so
if(!ui.initialized) return;
var match = kbd;
if(lg != '') match += ':' + lg;
if(kbd == '')
ui.KeyboardSelector.selectedIndex=0;
else
{
var opt=ui.KeyboardSelector.getElementsByTagName('OPTION');
for(i=0; i<opt.length; i++)
{
var t=opt[i].value;
if(lg == '') t = t.split(':')[0];
if(t == match)
{
ui.KeyboardSelector.selectedIndex=i;
break;
}
}
}
}
/**
* Function oskButtonState
* Scope Private
* @param {boolean} oskEnabled
* Description Update kbd icon border style to indicate whether OSK is enabled for display or not
**/
ui.oskButtonState = function(oskEnabled)
{
var s = ui.kbdIcon.style;
s.width='24px'; s.height='13px'; s.top='1px';
s.verticalAlign='bottom'; s.padding='2px 2px 1px 2px'; s.position='absolute';
s.border=oskEnabled ? 'inset 1px #808080' : 'none';
s.background=oskEnabled ? '#f7e7de' : 'white';
s.display = 'block';
if(ui.initialized) ui.oskButton.style.display = 'block';
}
/**
* Register all keymanweb and OSK events only after keymanweb is fully initialized
*
**/
ui.registerEvents = function()
{
/**
* 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 load event handler
*
* Set the menu selector to the currently saved keyboard when a keyboard is loaded
*
* Note: Cannot simply set it to the loaded keyboard,
* as more than one language may be supported by that keyboard.
*/
keymanweb['addEventListener']('keyboardloaded',
function(p) {
var sk = keymanweb['getSavedKeyboard']().split(':');
if(sk.length > 1) ui.updateMenu(sk[0],sk[1]);
});
/**
* Keyboard change event handler
*
* Update menu selection and control OSK display appropriately
*/
keymanweb['addEventListener']('keyboardchange',
function(p)
{
// Update the keyboard selector whenever a keyboard is loaded
ui.updateMenu(p['internalName'],p['languageCode']);
// (Conditionally) display the OSK button, and set the style
ui.addButtonOSK();
});
/**
* Show OSK event handler: show or hide the OSK button (never display if a CJK keyboard)
*/
osk['addEventListener']('show',
function(oskPosition)
{
ui.addButtonOSK();
return oskPosition;
});
/**
* Hide OSK event handler
*/
osk['addEventListener']('hide',
function(hiddenByUser)
{
if(ui.initialized) ui.oskButtonState(false);
});
}
/**
* Function _SelectorMouseDown
* Scope Private
* @param {Object} e event
* Description Set KMW UI activation state on mouse click
*/
ui._SelectorMouseDown = function(e)
{
if(keymanweb['activatingUI']) keymanweb['activatingUI'](1);
}
/**
* Function _SelectorMouseOver
* Scope Private
* @param {Object} e event
* Description Set KMW UI activation state on mouse over
*/
ui._SelectorMouseOver = function(e)
{
if(keymanweb['activatingUI']) keymanweb['activatingUI'](1);
}
/**
* Function _SelectorMouseOut
* Scope Private
* @param {Object} e event
* Description Clear KMW UI activation state on mouse out
*/
ui._SelectorMouseOut = function(e)
{
if(keymanweb['activatingUI']) keymanweb['activatingUI'](0);
}
/**
* Function _SelectKeyboardChange
* Scope Private
* @param {Object} e event
* Description Change active keyboard in response to user selection event
*/
ui.SelectKeyboardChange = function(e)
{
if(ui.KeyboardSelector.value != '-')
{
var i=ui.KeyboardSelector.selectedIndex;
var t=ui.KeyboardSelector.options[i].value.split(':');
keymanweb['setActiveKeyboard'](t[0],t[1]);
}
else
keymanweb['setActiveKeyboard']('');
//if(osk['show']) osk['show'](osk['isEnabled']()); handled by keyboard change event???
keymanweb['focusLastActiveElement']();
ui.selecting = true;
}
/**
* Function _SelectBlur
* Scope Private
* @param {Object} e event
* Description Ensure OSK is hidden when moving focus after reselecting a keyboard
*/
ui.SelectBlur = function(e)
{
if(!ui.selecting) keymanweb['focusLastActiveElement']();
ui.selecting = false;
}
/**
* Function ShowInterface
* Scope Private
* @param {number=} Px x-position for KMW window
* @param {number=} Py y-position for KMW window
* Description Display KMW window at specified position
*/
ui.ShowInterface = function(Px, Py)
{
if(!ui.initialized) return;
var Ls = ui.outerDiv.style;
if(Px && Py) {
Ls.left = Px + 'px';
Ls.top = Py + 'px';
}
Ls.display = 'block';
ui.kbdIcon.style.left = ui.KeyboardSelector.offsetWidth + 24 + 'px';
// (Conditionally) display the OSK button
ui.addButtonOSK();
// Set the language selection to the currently active keyboard, if listed
ui.updateMenu(keymanweb['getActiveKeyboard'](),keymanweb['getActiveLanguage']());
}
/**
* Function HideInterface
* Scope Private
* Description Completely hide KMW window
*/
ui.HideInterface = function()
{
if(!ui.initialized) return;
ui.outerDiv.style.display = 'none';
}
/**
* Function addButtonOSK
* Scope Private
* Description Display the OSK button unless a CJK keyboard (or English)
*/
ui.addButtonOSK = function()
{
if(ui.oskButton != null)
{
if(keymanweb['isCJK']() || (ui.KeyboardSelector.selectedIndex==0))
{
ui.oskButton.style.display = 'none';
ui.outerDiv.style.width = ui.KeyboardSelector.offsetWidth+30+'px';
}
else
{
ui.oskButton.style.display = 'block';
ui.oskButtonState(osk['isEnabled']());
ui.outerDiv.style.width = ui.KeyboardSelector.offsetWidth+56+'px';
}
}
}
//TODO: had to expose properties of params - what does that do? (focus event doesn't normally include these properties?)
keymanweb['addEventListener']('controlfocused',
function(params)
{
if(params['activeControl'] == null || params['activeControl']['LEnabled'])
{
/*if(keymanweb._IsIEEditableIframe(Ltarg))
Ltarg = Ltarg.ownerDocument.parentWindow.frameElement;
else if(keymanweb._IsMozillaEditableIframe(Ltarg))
Ltarg = Ltarg.defaultView.frameElement;*/
if(ui.floatRight) // I1296
ui.ShowInterface(util['getAbsoluteX'](params.target) + params.target.offsetWidth + 1, util['getAbsoluteY'](params.target) + 1);
else
ui.ShowInterface(util['getAbsoluteX'](params.target), util['getAbsoluteY'](params.target)
- parseInt(util['getStyleValue'](ui.outerDiv,'height'),10) - 2);
}
return true;
});
keymanweb['addEventListener']('controlblurred',
function(params)
{
if(!params['event']) return true; // I2404 - Manage IE events in IFRAMEs
if(!params['isActivating']) ui.HideInterface();
return true;
});
/**
* Function _Resize
* Scope Private
* @param {Object} e event object
* @return {boolean}
* Description Display KMW OSK at specified position (returns nothing)
*/
ui._Resize = function(e)
{
if(ui.outerDiv.style.display =='block')
{
var elem = keymanweb['getLastActiveElement']();
if(ui.floatRight) // I1296
ui.ShowInterface(util['getAbsoluteX'](elem) + elem.offsetWidth + 1, util['getAbsoluteY'](elem) + 1);
else
ui.ShowInterface(util['getAbsoluteX'](elem) + 1, util['getAbsoluteY'](elem) + elem.offsetHeight + 1);
}
return true;
}
if(window.addEventListener)
window.addEventListener('resize', ui._Resize, false);
else
window.attachEvent('onresize', ui._Resize);
// Initialize after KMW is fully initialized, if UI already loaded
keymanweb['addEventListener']('loaduserinterface',ui.Initialize);
// but also call initialization when script loaded, which is after KMW initialization for asynchronous script loading
ui.Initialize();
} catch(err){}
// Do not wrap in an anonymous function - let the closure compiler do that, but
// use try...catch to avoid script errors and only execute if a desktop browser
}