420 lines
13 KiB
JavaScript
420 lines
13 KiB
JavaScript
/*! jQuery UI Virtual Keyboard Typing Simulator v1.12.0 *//*
|
|
* for Keyboard v1.18+ only (2019-05-02)
|
|
*
|
|
* By Rob Garrison (aka Mottie)
|
|
* Licensed under the MIT License
|
|
*
|
|
* Use this extension with the Virtual Keyboard to simulate
|
|
* typing for tutorials or whatever else use you can find
|
|
*
|
|
* Requires:
|
|
* jQuery
|
|
* Keyboard plugin : https://github.com/Mottie/Keyboard
|
|
*
|
|
* Setup:
|
|
* $('.ui-keyboard-input')
|
|
* .keyboard(options)
|
|
* .addTyping(typing-options);
|
|
*
|
|
* // or if targeting a specific keyboard
|
|
* $('#keyboard1')
|
|
* .keyboard(options)
|
|
* .addTyping(typing-options);
|
|
*
|
|
* Basic Usage:
|
|
* // To disable manual typing on the virtual keyboard, just set "showTyping"
|
|
* // option to false
|
|
* $('#keyboard-input').keyboard(options).addTyping({ showTyping: false });
|
|
*
|
|
* // Change the default typing delay (time the virtual keyboard highlights the
|
|
* // manually typed key) - default = 250 milliseconds
|
|
* $('#keyboard-input').keyboard(options).addTyping({ delay: 500 });
|
|
*
|
|
* // get keyboard object, open it, then start typing simulation
|
|
* $('#keyboard-input').getkeyboard().reveal().typeIn('Hello World', 700);
|
|
*
|
|
* // get keyboard object, open it, type in "This is a test" with 700ms delay
|
|
* // between types, then accept & close the keyboard
|
|
* $('#keyboard-input')
|
|
* .getkeyboard()
|
|
* .reveal()
|
|
* .typeIn('This is a test', 700, function(keyboard) {
|
|
* keyboard.accept();
|
|
* });
|
|
*/
|
|
/* More Examples:
|
|
* $('#inter').getkeyboard().reveal().typeIn('\tHello \b\n\tWorld', 500);
|
|
* $('#meta')
|
|
* .getkeyboard().reveal()
|
|
* .typeIn('abCDd11123\u2648\u2649\u264A\u264B', 700, function() {
|
|
* alert('all done!');
|
|
* });
|
|
*/
|
|
/* jshint browser:true, jquery:true, unused:false */
|
|
/* global require:false, define:false, module:false */
|
|
;( function( factory ) {
|
|
if ( typeof define === 'function' && define.amd ) {
|
|
define( ['jquery'], factory );
|
|
} else if (
|
|
typeof module === 'object' &&
|
|
typeof module.exports === 'object'
|
|
) {
|
|
module.exports = factory( require( 'jquery' ) );
|
|
} else {
|
|
factory( jQuery );
|
|
}
|
|
}( function( $ ) {
|
|
$.fn.addTyping = function( options ) {
|
|
//Set the default values, use comma to separate the settings, example:
|
|
var defaults = {
|
|
showTyping : true,
|
|
lockTypeIn : false,
|
|
delay : 250,
|
|
hoverDelay : 250
|
|
},
|
|
$keyboard = $.keyboard;
|
|
return this.each( function() {
|
|
// make sure a keyboard is attached
|
|
var o, base = $( this ).data( 'keyboard' );
|
|
if ( !base ) {
|
|
return;
|
|
}
|
|
|
|
// variables
|
|
o = base.typing_options = $.extend( {}, defaults, options );
|
|
base.typing_keymap = {
|
|
' ' : 'space',
|
|
'"' : '34',
|
|
"'" : '39',
|
|
' ' : 'space',
|
|
'\b' : 'bksp', // delete character to the left
|
|
'{b}' : 'bksp',
|
|
'{d}' : 'del', // delete character to the right
|
|
'{l}' : 'left', // move caret left
|
|
'{r}' : 'right', // move caret right
|
|
'\n' : 'enter',
|
|
'\r' : 'enter',
|
|
'{e}' : 'enter',
|
|
'\t' : 'tab',
|
|
'{t}' : 'tab'
|
|
};
|
|
base.typing_xref = {
|
|
8 : 'bksp',
|
|
9 : 'tab',
|
|
13 : 'enter',
|
|
32 : 'space',
|
|
37 : 'left',
|
|
39 : 'right',
|
|
46 : 'del'
|
|
};
|
|
base.typing_event = false;
|
|
base.typing_namespace = base.namespace + 'typing';
|
|
base.extensionNamespace.push( base.typing_namespace );
|
|
// save lockInput setting
|
|
o.savedLockInput = base.options.lockInput;
|
|
|
|
base.typing_setup_reset = function() {
|
|
var kbevents = $keyboard.events,
|
|
namespace = base.typing_namespace,
|
|
events = [ kbevents.kbHidden, kbevents.kbInactive, '' ]
|
|
.join( namespace + ' ' );
|
|
// reset "typeIn" when keyboard is closed
|
|
base.$el
|
|
.unbind( namespace )
|
|
.bind( events, function() {
|
|
base.typing_reset();
|
|
});
|
|
base
|
|
.unbindButton( namespace )
|
|
.bindButton( 'mousedown' + namespace, function() {
|
|
base.typing_reset();
|
|
});
|
|
};
|
|
|
|
base.typing_setup = function() {
|
|
var namespace = base.typing_namespace;
|
|
base.typing_setup_reset();
|
|
base.$el
|
|
.bind( $keyboard.events.kbBeforeVisible + namespace, function() {
|
|
base.typing_setup();
|
|
});
|
|
base.$preview
|
|
.unbind( namespace )
|
|
.bind( 'keyup' + namespace, function( e ) {
|
|
if ( o.init && o.lockTypeIn || !o.showTyping ) {
|
|
return false;
|
|
}
|
|
if ( e.which >= 37 && e.which <=40 ) {
|
|
return; // ignore arrow keys
|
|
}
|
|
if ( e.which === 16 ) {
|
|
base.shiftActive = false;
|
|
}
|
|
if ( e.which === 18 ) {
|
|
base.altActive = false;
|
|
}
|
|
if ( e.which === 16 || e.which === 18 ) {
|
|
base.showSet();
|
|
// Alt key will shift focus to the menu - doesn't work in Windows
|
|
setTimeout( function() {
|
|
if (base.$preview) {
|
|
base.$preview.focus();
|
|
}
|
|
}, 200 );
|
|
return;
|
|
}
|
|
})
|
|
// change keyset when either shift or alt is held down
|
|
.bind( 'keydown' + namespace, function( e ) {
|
|
if ( o.init && o.lockTypeIn || !o.showTyping ) {
|
|
return false;
|
|
}
|
|
e.temp = false; // prevent repetitive calls while keydown repeats.
|
|
if ( e.which === 16 ) {
|
|
e.temp = !base.shiftActive; base.shiftActive = true;
|
|
}
|
|
// it should be ok to reset e.temp, since both alt and shift will
|
|
// call this function separately
|
|
if ( e.which === 18 ) {
|
|
e.temp = !base.altActive; base.altActive = true;
|
|
}
|
|
if ( e.temp ) {
|
|
base.showSet();
|
|
base.$preview.focus(); // Alt shift focus to the menu
|
|
}
|
|
base.typing_event = true;
|
|
// Simulate key press for tab and backspace since they don't fire
|
|
// the keypress event
|
|
if ( base.typing_xref[ e.which ] ) {
|
|
base.typing_findKey( '', e ); // pass event object
|
|
}
|
|
|
|
})
|
|
.bind( 'keypress' + namespace, function( e ) {
|
|
if ( o.init && o.lockTypeIn ) {
|
|
return false;
|
|
}
|
|
// Simulate key press on virtual keyboard
|
|
if ( base.typing_event && !base.options.lockInput ) {
|
|
base.typing_reset();
|
|
base.typing_event = true;
|
|
base.typing_findKey( '', e ); // pass event object
|
|
}
|
|
});
|
|
};
|
|
|
|
base.typing_reset = function() {
|
|
base.typing_event = o.init = false;
|
|
o.text = '';
|
|
o.len = o.current = 0;
|
|
base.options.lockInput = o.savedLockInput;
|
|
// clearTimeout( base.typing_timer );
|
|
};
|
|
|
|
// Store typing text
|
|
base.typeIn = function( txt, delay, callback, e ) {
|
|
if ( !base.isVisible() ) {
|
|
// keyboard was closed
|
|
clearTimeout( base.typing_timer );
|
|
base.typing_reset();
|
|
return;
|
|
}
|
|
if ( !base.typing_event ) {
|
|
|
|
if ( o.init !== true ) {
|
|
o.init = true;
|
|
base.options.lockInput = o.lockTypeIn;
|
|
o.text = txt || o.text || '';
|
|
o.len = o.text.length;
|
|
o.delay = delay || o.delay;
|
|
o.current = 0; // position in text string
|
|
if ( callback ) {
|
|
o.callback = callback;
|
|
}
|
|
}
|
|
// function that loops through and types each character
|
|
txt = o.text.substring( o.current, ++o.current );
|
|
// add support for curly-wrapped single character: {l}, {r}, {d}, etc.
|
|
if (
|
|
txt === '{' &&
|
|
o.text.substring( o.current + 1, o.current + 2 ) === '}'
|
|
) {
|
|
txt += o.text.substring( o.current, o.current += 2 );
|
|
}
|
|
base.typing_findKey( txt, e );
|
|
} else if ( typeof txt === 'undefined' ) {
|
|
// typeIn called by user input
|
|
base.typing_event = false;
|
|
base.options.lockInput = o.savedLockInput;
|
|
return;
|
|
}
|
|
|
|
};
|
|
|
|
base.typing_findKey = function( txt, e ) {
|
|
var tar, m, n, k, key, ks, meta, set,
|
|
kbcss = $keyboard.css,
|
|
mappedKeys = $keyboard.builtLayouts[base.layout].mappedKeys;
|
|
// stop if keyboard is closed
|
|
if ( !base.isOpen || !base.$keyboard.length ) {
|
|
return;
|
|
}
|
|
ks = base.$keyboard.find( '.' + kbcss.keySet );
|
|
k = txt in base.typing_keymap ? base.typing_keymap[ txt ] : txt;
|
|
// typing_event is true when typing on the actual keyboard - look for
|
|
// actual key; All of this breaks when the CapLock is on... unable to
|
|
// find a cross-browser method that works.
|
|
tar = '.' + kbcss.keyButton + '[data-action="' + k + '"]';
|
|
if ( base.typing_event && e ) {
|
|
// xref used for keydown
|
|
// ( 46 = delete in keypress & period on keydown )
|
|
if (
|
|
e.type !== 'keypress' &&
|
|
base.typing_xref.hasOwnProperty( e.keyCode || e.which )
|
|
) {
|
|
// special named keys: bksp, tab and enter
|
|
tar = '.' +
|
|
kbcss.keyPrefix +
|
|
base.processName( base.typing_xref[ e.keyCode || e.which ] );
|
|
} else {
|
|
m = String.fromCharCode( e.charCode || e.which );
|
|
tar = ( mappedKeys.hasOwnProperty( m ) ) ?
|
|
'.' + kbcss.keyButton + '[data-value="' +
|
|
mappedKeys[ m ].replace(/"/g, '\\"') + '"]' :
|
|
'.' + kbcss.keyPrefix + base.processName( m );
|
|
}
|
|
}
|
|
// find key
|
|
key = ks.filter( ':visible' ).find( tar );
|
|
if ( key.length ) {
|
|
// key is visible, simulate typing
|
|
base.typing_simulateKey( key, txt, e );
|
|
} else {
|
|
// key not found, check if it is in the keymap
|
|
// (tab, space, enter, etc)
|
|
if ( base.typing_event ) {
|
|
key = ks.find( tar );
|
|
} else {
|
|
// key not found, check if it is in the keymap
|
|
// (tab, space, enter, etc)
|
|
n = txt in base.typing_keymap ?
|
|
base.typing_keymap[ txt ] :
|
|
base.processName( txt );
|
|
// find actual key on keyboard
|
|
key = ks.find( '.' + kbcss.keyPrefix + n );
|
|
}
|
|
// find the keyset
|
|
set = key.closest( '.' + kbcss.keySet );
|
|
// figure out which keyset the key is in then simulate clicking on
|
|
// that meta key, then on the key
|
|
if ( set.attr('name' ) ) {
|
|
if ( o.showTyping ) {
|
|
// get meta key name
|
|
meta = set.attr( 'name' );
|
|
// show correct key set
|
|
base.shiftActive = /shift/.test( meta );
|
|
base.altActive = /alt/.test( meta );
|
|
base.metaActive = base.last.keyset[ 2 ] = /\bmeta/.test(meta) ?
|
|
( meta ).match(/meta[\w-]+/)[0] : false;
|
|
base.showSet( base.metaActive );
|
|
}
|
|
// Add the key
|
|
base.typing_simulateKey( key, txt, e );
|
|
} else {
|
|
if ( !base.typing_event ) {
|
|
// Key doesn't exist on the keyboard, so just enter it
|
|
if (
|
|
txt in base.typing_keymap &&
|
|
base.typing_keymap[txt] in $keyboard.keyaction
|
|
) {
|
|
$keyboard.keyaction[ base.typing_keymap[ txt ] ]( base, key, e );
|
|
} else {
|
|
base.insertText( txt );
|
|
}
|
|
base.checkCombos();
|
|
base.$el.trigger( $keyboard.events.kbChange, [ base, base.el ] );
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( o.current <= o.len && o.len !== 0 ) {
|
|
if ( !base.isVisible() ) {
|
|
return; // keyboard was closed, abort!!
|
|
}
|
|
base.typing_timer = setTimeout( function() {
|
|
base.typeIn();
|
|
}, o.delay );
|
|
} else if ( o.len !== 0 ) {
|
|
// o.len is zero when the user typed on the actual keyboard during
|
|
// simulation
|
|
base.typing_reset();
|
|
if ( typeof o.callback === 'function' ) {
|
|
// ensure all typing animation is done before the callback
|
|
base.typing_timer = setTimeout( function() {
|
|
// if the user typed during the key simulation, the "o" variable
|
|
// may sometimes be undefined
|
|
if ( typeof o.callback === 'function' ) {
|
|
o.callback( base );
|
|
}
|
|
}, o.delay );
|
|
}
|
|
return;
|
|
} else {
|
|
base.typing_reset();
|
|
}
|
|
};
|
|
|
|
// mouseover the key, add the text directly, then mouseout on the key
|
|
base.typing_simulateKey = function( el, txt, e ) {
|
|
var len = el.length;
|
|
if ( !base.isVisible() ) {
|
|
return;
|
|
}
|
|
if ( o.showTyping && len ) {
|
|
el.filter( ':visible' ).trigger( 'mouseenter' + base.namespace );
|
|
if ( o.showTyping && len ) {
|
|
setTimeout( function() {
|
|
el.trigger( 'mouseleave' + base.namespace );
|
|
}, Math.min( o.hoverDelay, o.delay ) );
|
|
}
|
|
}
|
|
if ( !base.typing_event ) {
|
|
// delay required or initial tab does not get added
|
|
// in the main demo (international keyboard)
|
|
setTimeout( function() {
|
|
if (
|
|
txt in base.typing_keymap &&
|
|
base.typing_keymap[ txt ] in $keyboard.keyaction
|
|
) {
|
|
e = e || $.Event( 'keypress' );
|
|
e.target = el; // "Enter" checks for the e.target
|
|
$keyboard.keyaction[ base.typing_keymap[ txt ] ]( base, el, e );
|
|
} else {
|
|
base.insertText( txt );
|
|
}
|
|
base.checkCombos();
|
|
base.$el.trigger( $keyboard.events.kbChange, [ base, base.el ] );
|
|
}, o.delay/3 );
|
|
}
|
|
};
|
|
|
|
// visible event is fired before this extension is initialized, so check!
|
|
if ( o.showTyping && base.options.alwaysOpen && base.isVisible() ) {
|
|
base.typing_setup();
|
|
} else {
|
|
// capture and simulate typing
|
|
base.$el
|
|
.unbind( $keyboard.events.kbBeforeVisible + base.typing_namespace )
|
|
.bind( $keyboard.events.kbBeforeVisible + base.typing_namespace, function() {
|
|
if ( o.showTyping ) {
|
|
base.typing_setup();
|
|
} else {
|
|
base.typing_setup_reset();
|
|
}
|
|
});
|
|
}
|
|
});
|
|
};
|
|
|
|
}));
|