Files
Chamilo/app/Resources/public/assets/keyboard/js/jquery.keyboard.extension-all.js
2025-08-14 22:33:03 +02:00

2468 lines
80 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*** This file is dynamically generated ***
█████▄ ▄████▄ █████▄ ▄████▄ ██████ ███████▄ ▄████▄ █████▄ ██ ██████ ██ ██
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀▀██
█████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀
*/
/*! jQuery UI Virtual Keyboard (1.30.2) - ALL Extensions + Mousewheel */
/*! jQuery UI Virtual Keyboard Alt Key Popup v2.0.0 *//*
* for Keyboard v1.18+ only (2018-04-19)
*
* By Rob Garrison (aka Mottie)
* Licensed under the MIT License
*
* Note: Use of `event.key` requires a modern browser
* (https://caniuse.com/#feat=keyboardevent-key)
*/
/* 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( $ ) {
'use strict';
var $keyboard = $.keyboard;
$.extend( $keyboard.css, {
altKeyPopup : 'ui-keyboard-popup',
altKeyOverlay : 'ui-keyboard-overlay',
altKeyPopupOpen : 'ui-keyboard-popup-open'
});
$keyboard.altKeys = $.extend({
a : '\u00e5 \u00e6 \u0101 \u0103 \u0105 \u00e0 \u00e1 \u00e2 \u00e3 \u00e4', // å æ ā ă ą à á â ã ä
A : '\u00c5 \u00c6 \u0100 \u0102 \u0104 \u00c0 \u00c1 \u00c2 \u00c3 \u00c4', // Å Æ Ā Ă Ą À Á Â Ã Ä
c : '\u00e7 \u0107 \u0109 \u010b \u010d', // ç ć ĉ ċ č
C : '\u00c7 \u0106 \u0108 \u010a \u010c', // Ç Ć Ĉ Ċ Č
d : '\u010f \u00f0 \u010f', // ď ð ď
D : '\u010e \u00d0 \u010e', // Ď Ð Ď
e : '\u0117 \u0119 \u0115 \u011b \u0259 \u00e8 \u00e9 \u00ea \u00eb \u0113', // ė ę ĕ ě ə è é ê ë ē
E : '\u0116 \u0118 \u0114 \u011a \u018e \u00c8 \u00c9 \u00ca \u00cb \u0112', // Ė Ę Ĕ Ě Ǝ È É Ê Ë Ē
g : '\u0123 \u011f \u011d \u0121', // ģ ğ ĝ ġ
G : '\u0122 \u011e \u011c \u0120', // Ģ Ğ Ĝ Ġ
h : '\u0125 \u0127', // ĥ ħ
H : '\u0124 \u0126', // Ĥ Ħ
i : '\u0131 \u012f \u012b \u00ef \u00ee \u00ed \u00ec \u0129 \u012d', // ı į ī ï î í ì ĩ ĭ
I : '\u0130 \u012e \u012a \u00cf \u00ce \u00cd \u00cc \u0128 \u012c', // İ Į Ī Ï Î Í Ì Ĩ Ĭ
j : '\u0135', // ĵ
J : '\u0134', // Ĵ
k : '\u0137', // ķ
K : '\u0136', // Ķ
l : '\u0141 \u013d \u013b \u0139 \u013f', // Ł Ľ Ļ Ĺ Ŀ
L : '\u0142 \u013e \u013c \u013a \u0140', // ł ľ ļ ĺ ŀ
n : '\u0149 \u0148 \u0146 \u0144 \u00f1', // ʼn ň ņ ń ñ
N : '\u0149 \u0147 \u0145 \u0143 \u00d1', // ʼn Ň Ņ Ń Ñ
o : '\u0153 \u0151 \u00f8 \u00f6 \u00f5 \u00f4 \u00f3 \u00f2 \u014d \u014f', // œ ő ø ö õ ô ó ò ō ŏ
O : '\u0152 \u0150 \u00d8 \u00d6 \u00d5 \u00d4 \u00d3 \u00d2 \u014c \u014e', // Œ Ő Ø Ö Õ Ô Ó Ò Ō Ŏ
r : '\u0155 \u0159 \u0157', // ŕ ř ŗ
R : '\u0154 \u0158 \u0156', // Ŕ Ř Ŗ
s : '\u015b \u0161 \u015f \u00df \u00a7 \u015d', // ś š ş ß § ŝ
S : '\u015a \u0160 \u015e \u1e9e \u00a7 \u015c', // Ś Š Ş ẞ § Ŝ
t : '\u00fe \u0165 \u021b \u0163 \u0167', // þ ť ț ţ ŧ
T : '\u00de \u0164 \u021a \u0162 \u0166', // Þ Ť Ț Ţ Ŧ
u : '\u0173 \u0171 \u016f \u016b \u00fc \u00fb \u00fa \u00f9 \u0169 \u016d', // ų ű ů ū ü û ú ù ũ ŭ
U : '\u0172 \u0170 \u016e \u016a \u00dc \u00db \u00da \u00d9 \u0168 \u016c', // Ų Ű Ů Ū Ü Û Ú Ù Ũ Ŭ
w : '\u0175', // ŵ
W : '\u0174', // Ŵ
y : '\u00fd', // ý
Y : '\u00dd', // Ý
z : '\u017a \u017c \u017e', // ź ż ž
Z : '\u0179 \u017b \u017d', // Ź Ż Ž
'!' : '\u00a1', // ¡
'$' : '\u20ac \u00a3 \u00a4 \u00a5 \u00a2 \u20a1 \u20b1 \u20a9 \u20b9 \u20aa \u20ad \u20ae \u20a6 \u20a4', // €£¤¥¢₡₱₩₹₪₭₮₦₤
'?' : '\u00bf', // ¿
"'" : '\u3008 \u300c \u300e \u201c', // 〈 「 『 “
'"' : '\u3009 \u300d \u300f \u201d', // 〉 」 』 ”
'(' : '\u300a \u3010 \u3014', // « 【
')' : '\u300b \u3011 \u3015' // » 】
}, $keyboard.altKeys );
$.fn.addAltKeyPopup = function( options ) {
//Set the default values, use comma to separate the settings, example:
var defaults = {
// time to hold down a button in ms to trigger a popup
holdTime : 500,
// events triggered when popup is visible & hidden
popupVisible : 'popup-visible',
popupHidden : 'popup-hidden',
popupPosition : null
};
return this.each( function() {
// make sure a keyboard is attached
var base = $( this ).data( 'keyboard' );
if (!base) { return; }
// variables
base.altkeypopup_options = $.extend(
{},
defaults,
base.altkeypopup_options, // restore prev options on layout update
options
);
// already initialized
if ( base.altkeypopup_namespace ) {
return base.altkeypopup_setup();
}
base.altkeypopup_namespace = base.namespace + 'AltKeyPopup';
base.extensionNamespace.push( base.altkeypopup_namespace );
base.altkeypopup_setup = function() {
var timer,
start = 'mousedown touchstart '
.split( ' ' )
.join( base.altkeypopup_namespace + ' ' ),
end = 'mouseup touchend touchcancel '
.split( ' ' )
.join( base.altkeypopup_namespace + ' ' );
// force disable repeat keys
base.options.repeatRate = 0;
// add hold key functionality for popups
base
.unbindButton( base.altkeypopup_namespace )
.bindButton( start, function() {
clearTimeout( timer );
var $key = $( this ),
key = $key.attr( 'data-value' ) || '',
delay = base.altkeypopup_options.holdTime;
if ( key in $keyboard.altKeys ) {
if (delay) {
timer = setTimeout( function() {
base.altKeyPopup_popup( key, $key );
}, delay );
} else {
// holdTime set to zero.. don't use a setTimeout
base.altKeyPopup_popup( key, $key );
}
}
})
.bindButton( end, function() {
clearTimeout( timer );
});
base.altkeypopup_blockingFlag = false;
base.$preview
.unbind(
'keypress keydown keyup '
.split( ' ' )
.join( base.altkeypopup_namespace + ' ' )
.trim()
)
.bind(
'keypress keydown keyup '
.split( ' ' )
.join( base.altkeypopup_namespace + ' ' ),
function( event ) {
if ( event.type === 'keyup' ) {
clearTimeout( timer );
base.altkeypopup_blockingFlag = false;
return event.key !== 'Escape';
}
var layout = $keyboard.builtLayouts[ base.layout ],
$key = $( event.target ),
origKey = event.key,
key = event.key;
if ( event.type === 'keydown' && key in $keyboard.altKeys ) {
// Compare typed key to prevent blocking issues reported in #664
if ( base.altkeypopup_blockingFlag === origKey ) {
return false;
}
base.altkeypopup_blockingFlag = origKey;
// return true on initial keydown or keypress never fires
// then return false to prevent repeat key
return true;
}
if ( base.altkeypopup_blockingFlag ) {
// find mapped key, if any
if (
layout.hasMappedKeys &&
layout.mappedKeys.hasOwnProperty( key )
) {
key = layout.mappedKeys[ key ];
}
if ( key in $keyboard.altKeys ) {
clearTimeout( timer );
timer = setTimeout( function() {
if ( base.altkeypopup_blockingFlag === origKey ) {
base.altKeyPopup_popup( key, $key );
}
}, base.altkeypopup_options.holdTime );
}
return true;
}
}
);
};
base.altKeyPopup_close = function() {
base.altkeypopup_blockingFlag = false;
base.altKeyPopup_$overlay = null;
setTimeout(function() {
if (base.$keyboard.length) {
base.$keyboard.removeClass($keyboard.css.altKeyPopupOpen);
var $el = base.$keyboard.find( '.' + $keyboard.css.altKeyOverlay );
if ($el) {
$el.remove();
}
}
}, 1);
$( document ).unbind( base.altkeypopup_namespace );
base.$preview.focus();
// restore ignoreEsc option
base.options.ignoreEsc = base.altKeyPopup_savedIgnoreEsc;
// trigger popup hidden event
base.$el.trigger( base.altkeypopup_options.popupHidden, [ base ] );
};
base.altKeyPopup_popup = function( key, $key ) {
if ( base.$keyboard.find( '.' + $keyboard.css.altKeyOverlay ).length ) {
return;
}
var keys, $container, $keys, positionHoriz, positionVert, top,
popupWidth, popupHeight, evts,
kbcss = $keyboard.css,
data = {
$kb : base.$keyboard,
kbWidth : base.$keyboard.outerWidth(),
kbHeight : base.$keyboard.outerHeight(),
$key : $key
};
// overlay keyboard
base.altKeyPopup_$overlay =
$( '<div class="' + kbcss.altKeyOverlay + '" />' )
.css({
width : data.kbWidth,
height: data.kbHeight
})
.appendTo( base.$keyboard )
.bind( 'click touchstart', function() {
base.altKeyPopup_close();
});
evts = 'inactive hidden '
.split( ' ' )
.join( base.altkeypopup_namespace + ' ' );
base.$keyboard.addClass($keyboard.css.altKeyPopupOpen);
base.$el.unbind( evts ).bind( evts, function() {
base.altKeyPopup_close();
});
// remove character added when key was initially pressed, unless it
// was a backspace key
if ( key !== 'bksp' ) {
$keyboard.keyaction.bksp( base );
}
// make popup; use the same classes as the keyboard container
$container = $(
'<div class="' + kbcss.altKeyPopup + ' ' +
base.options.css.container + '" />'
);
keys = $keyboard.altKeys[ key ].split( /\s+/ );
// make popup keys
base.buildRow( $container, 0, keys, [] );
// add popup & add bindings
$keys = $container
.appendTo( base.altKeyPopup_$overlay )
.children()
.bind( 'mousedown touchstart', function() {
// action/value now processed by core functions
base.altKeyPopup_close();
})
.bind( 'mouseover mouseleave', function( event ){
// remove hover from physical keyboard highlighted key
$keys.removeClass( base.options.css.buttonHover );
if ( event.type !== 'mouseleave' ) {
$( this ).addClass( base.options.css.buttonHover );
}
});
// popup opened... add key highlight
base.altKeyPopup_navigate( true ); // init
// set ignoreEsc to allow escape to ONLY close the popup
base.altKeyPopup_savedIgnoreEsc = base.options.ignoreEsc;
base.options.ignoreEsc = true;
$( document )
.unbind( base.altkeypopup_namespace )
.bind( 'keydown' + base.altkeypopup_namespace, function() {
// keep home & end from scrolling the page
return false;
})
.bind( 'keyup' + base.altkeypopup_namespace, function( event ) {
if ( event.key === 'Escape' ) {
event.which = 0; // prevent escClose from closing the keyboard
base.altKeyPopup_close();
} else {
base.altKeyPopup_navigate( event );
}
return false;
});
data.$popup = $container;
popupWidth = $container.outerWidth();
// position popup within $keyboard container
positionHoriz = $key.position().left - popupWidth / 2;
if ( positionHoriz + popupWidth > data.kbWidth ) {
positionHoriz = data.kbWidth - popupWidth;
if ( positionHoriz < 0 ) {
$container.css({
width : data.kbWidth,
height : 'auto'
});
}
}
positionVert = $key.position().top - $key.outerHeight() - 5;
popupHeight = $container.outerHeight();
// find top of keyset (don't cover up the preview input)
top = base.$keyboard.find( '.' + kbcss.keySet ).position().top;
if ( positionVert + popupHeight > data.kbHeight ) {
positionVert = data.kbHeight - popupHeight;
if ( positionVert < top ) {
$container.css({
height : data.popupHeight,
width : 'auto'
});
}
}
data.popupWidth = $container.outerWidth();
data.popupHeight = $container.outerHeight();
data.popupLeft = positionHoriz < 0 ? 0 : positionHoriz;
data.popupTop = positionVert < top ? top : positionVert;
$container.css({
position : 'absolute',
left : data.popupLeft,
top : data.popupTop
});
// adjust position as needed using popupPosition callback function
if ( typeof base.altkeypopup_options.popupPosition === 'function' ) {
base.altkeypopup_options.popupPosition(base, data);
}
base.$preview.blur();
// trigger popup visible event
base.$el.trigger( base.altkeypopup_options.popupVisible, [ base ] );
};
base.altKeyPopup_navigate = function( event ) {
var indx,
kbcss = $keyboard.css,
k = $keyboard.navigationKeys,
hover = base.options.css.buttonHover,
$keys = base.$keyboard
.find( '.' + kbcss.altKeyPopup )
.find( '.' + kbcss.keyButton ),
max = $keys.length - 1;
// popup visible, add key highlight
if ( event === true ) {
$keys.eq( 0 ).addClass( hover );
base.altKeyPopup_currentIndex = 0;
return;
}
indx = base.altKeyPopup_currentIndex;
if ( event.key === 'Enter' ) {
base.insertText( $keys.eq( indx ).attr( 'data-value' ) );
base.altKeyPopup_close();
return true;
}
switch( event.key ) {
case 'End': indx = max; break;
case 'Home': indx = 0; break;
case 'ArrowLeft': indx -= 1; break;
case 'ArrowRight': indx += 1; break;
}
if ( indx < 0 ) { indx = 0; }
if ( indx > max ) { indx = max; }
base.altKeyPopup_currentIndex = indx;
$keys
.removeClass( hover )
.eq( indx )
.addClass( hover );
};
// visible event is fired before this extension is initialized, so check!
if ( base.options.alwaysOpen && base.isVisible() ) {
base.altkeypopup_setup();
}
// setup altkey popup
base.$el
.unbind(
$keyboard.events.kbBeforeVisible + base.altkeypopup_namespace
)
.bind(
$keyboard.events.kbBeforeVisible + base.altkeypopup_namespace,
function() {
base.altkeypopup_setup();
}
);
});
};
}));
/*! jQuery UI Virtual Keyboard Autocomplete v1.11.4 *//*
* for Keyboard v1.18+ only (2018-01-10)
*
* By Rob Garrison (Mottie)
* Licensed under the MIT License
*
* Use this extension with the Virtual Keyboard to get
* the jQuery UI Autocomplete widget to work seamlessly
*
* Requires:
* jQuery
* jQuery UI & css
* Keyboard plugin : https://github.com/Mottie/Keyboard
*
* Setup:
* $('.ui-keyboard-input')
* .keyboard(options)
* .autocomplete(options)
* .addAutoComplete();
*
* // or if targeting a specific keyboard
* $('#keyboard1')
* .keyboard(options) // keyboard plugin
* .autocomplete(options) // jQuery UI autocomplete
* .addAutoComplete(); // this keyboard extension
*
*/
/*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($) {
'use strict';
$.fn.addAutocomplete = function(options) {
var defaults = {
position : {
of : null,
my : 'right top',
at : 'left top',
collision: 'flip'
},
events: 'autocomplete',
data: ''
};
return this.each(function() {
// make sure a keyboard is attached
var o, namespace,
base = $(this).data('keyboard');
if (!base) { return; }
namespace = base.namespace + 'Autocomplete';
base.autocomplete_namespace = namespace;
base.extensionNamespace.push( namespace );
// Setup
base.autocomplete_init = function() {
// variables
o = base.autocomplete_options = $.extend( true, {}, defaults, options );
var events = o.events || o.data || 'autocomplete';
// visible event is fired before this extension is initialized, so check!
if (base.options.alwaysOpen && base.isVisible()) {
base.autocomplete_setup();
}
base.$el
.unbind(namespace)
.bind($.keyboard.events.kbVisible + namespace, function() {
base.autocomplete_setup();
})
.bind($.keyboard.events.kbHidden + namespace, function() {
base.$el[o.data || 'autocomplete']('close');
})
.bind($.keyboard.events.kbChange + namespace, function() {
if (base.hasAutocomplete && base.isVisible()) {
base.$el.val(base.$preview.val());
}
})
.bind(events + 'open' + namespace, function() {
if (base.hasAutocomplete) {
// default to $keyboard if no position.of defined
var position = $.extend( {}, o.position );
// refresh base.$keyboard (it gets destroyed after use); fixes #382
position.of = position.of || base.$keyboard;
// reposition autocomplete window next to the keyboard
base.$autocomplete.menu.element.position( position );
}
})
.bind(events + 'select' + namespace, function(e, ui) {
base.autocomplete_getVal(ui.item);
});
};
base.autocomplete_getVal = function(val) {
var v;
switch (typeof val) {
case 'string':
v = val || '';
break;
case 'object':
v = val.label || val.value || '';
break;
default:
v = base.preview && base.preview.value || base.el.value;
}
v = v.toString();
if (base.hasAutocomplete && v !== '') {
// fallback to original input if undefined, see #520
(base.$preview || base.$el)
.val( v )
.focus();
// see issue #95 - thanks banku!
base.last.start = v.length;
base.last.end = v.length;
base.last.val = v;
}
};
base.autocomplete_update = function(event) {
clearTimeout( base.$autocomplete.searching );
base.$autocomplete.searching = setTimeout(function() {
// only search if the value has changed
if ( base.$autocomplete.term !== base.$autocomplete.element.val() ) {
base.$autocomplete.selectedItem = null;
base.$autocomplete.search( null, event );
}
}, base.$autocomplete.options.delay );
};
base.autocomplete_navKeys = {
8: 'backSpace',
9: 'tab',
13: 'enter',
20: 'capsLock',
27: 'escape',
32: 'space',
33: 'pageup',
34: 'pagedown',
35: 'end',
36: 'home',
37: 'left',
38: 'up',
39: 'right',
40: 'down',
45: 'insert',
46: 'delete'
};
// set up after keyboard is visible
base.autocomplete_setup = function() {
var key;
// look for autocomplete
base.$autocomplete = base.$el.data(base.autocomplete_options.data) ||
// data changes based on jQuery UI version
base.$el.data('uiAutocomplete') ||
base.$el.data('ui-autocomplete') ||
base.$el.data('autocomplete');
base.hasAutocomplete = (typeof(base.$autocomplete) === 'undefined') ?
false : (base.$autocomplete.options.disabled) ? false : true;
// only bind to keydown once
if (base.hasAutocomplete) {
base.$preview.bind('keydown' + namespace + ' keypress' + namespace, function(event) {
// send keys to the autocomplete widget (arrow, pageup/down, etc)
if (base.$preview && event.namespace !== base.$autocomplete.eventNamespace) {
event.namespace = base.$autocomplete.eventNamespace.slice(1);
key = base.autocomplete_navKeys[event.which];
if (key) {
if (base.el !== base.preview) {
base.$el.triggerHandler(event);
if (key === 'enter') {
// update preview with the selected item
setTimeout(function(){
if (base.$autocomplete) {
base.$preview.val(base.$autocomplete.selectedItem.value);
base.$preview.focus();
}
}, 100);
}
}
} else {
// only search when a non-navigation key is pressed
base.autocomplete_update(event);
}
}
});
var events = 'mouseup mousedown mouseleave touchstart touchend touchcancel '
.split(' ')
.join(namespace + ' ');
base.bindButton(events, function(event) {
base.autocomplete_update(event);
});
}
if (!base.escCloseCallback.autocomplete) {
base.escCloseCallback.autocomplete = base.checkAutocompleteMenu;
}
};
base.checkAutocompleteMenu = function($target) {
// prevent selecting an item in autocomplete from closing keyboard
// return a "shouldStayOpen" boolean state for this extension
return base.hasAutocomplete &&
$target.closest('ul').hasClass('ui-autocomplete');
};
base.autocomplete_destroy = function() {
clearTimeout(base.$autocomplete.searching);
base.hasAutocomplete = false;
base.$el.unbind(namespace);
if (base.$preview) {
base.$preview.unbind(namespace);
base.unbindButton(namespace);
}
delete base.$autocomplete;
};
base.autocomplete_init();
});
};
}));
/*! jQuery UI Virtual Keyboard Virtual Caret v1.1.5 (beta) *//*
* for Keyboard v1.18+ only (2/20/2016)
* modified from https://github.com/component/textarea-caret-position
*
* By Rob Garrison (aka Mottie)
* Licensed under the MIT License
*
* CSS changes
* NOTE: caret margin-top => is added to the caret height (top & bottom)
* .ui-keyboard-preview-wrapper { position: relative; overflow: hidden; }
* .ui-keyboard-caret { background: red; width: 1px; margin-top: 3px; }
*/
/*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( $ ) {
'use strict';
var $keyboard = $.keyboard;
$keyboard.firefox = typeof window.mozInnerScreenX !== 'undefined';
$.extend( $keyboard.css, {
caret : 'ui-keyboard-caret',
caretMirror : 'ui-keyboard-mirror-div'
});
$.fn.addCaret = function( options ) {
var defaults = {
caretClass : '',
// *** for future use ***
// data-attribute containing the character(s) next to the caret
charAttr : 'data-character',
// # character(s) next to the caret (can be negative for RTL)
charIndex : 1,
offsetX : 0,
offsetY : 0,
adjustHt : 0
};
return this.each( function() {
// make sure a keyboard is attached
var o, namespace,
kbevents = $keyboard.events,
base = $( this ).data( 'keyboard' );
if ( !base ) { return; }
// variables
o = base.caret_options = $.extend( {}, defaults, options );
namespace = base.caret_namespace = base.namespace + 'caret';
base.extensionNamespace.push( namespace );
// modified from https://github.com/component/textarea-caret-position
// The properties that we copy into a mirrored div.
// Note that some browsers, such as Firefox,
// do not concatenate properties, i.e. padding-top, bottom etc. -> padding,
// so we have to do every single property specifically.
base.textareaCaretProperties = [
'direction', 'boxSizing', 'width', 'height', 'overflowX', 'overflowY',
'borderTopWidth', 'borderRightWidth', 'borderBottomWidth', 'borderLeftWidth', 'borderStyle',
'paddingTop', 'paddingRight', 'paddingBottom', 'paddingLeft',
'fontStyle', 'fontVariant', 'fontWeight', 'fontStretch', 'fontSize', 'fontSizeAdjust',
'lineHeight', 'fontFamily', 'textAlign', 'textTransform', 'textIndent', 'textDecoration',
'letterSpacing', 'wordSpacing', 'tabSize', 'MozTabSize'
];
base.caret_setup = function() {
var kbcss = $keyboard.css,
events = 'keyup keypress mouseup mouseleave '.split( ' ' ).join( namespace + ' ' ),
style = 'position:absolute;visibility:hidden;top:-9999em;left:-9999em;' +
'white-space:pre-wrap;z-index:-10;' +
( base.preview.nodeName === 'INPUT' ? '' : 'word-wrap:break-word;' );
// add mirrored div
base.$keyboard.find( '.' + kbcss.caretMirror ).remove();
base.caret_$div = $( '<div class="' + kbcss.caretMirror + '" style="' + style + '">' )
.appendTo( base.$keyboard );
// remove caret, just-in-case
if (base.$caret) { base.$caret.remove(); }
base.$caret = $( '<div class="' + kbcss.caret + ' ' + o.caretClass + '" style="position:absolute;">' )
.insertAfter( base.$preview );
base.$el
.unbind( kbevents.kbChange + namespace )
.bind( kbevents.kbChange + namespace, function() {
base.findCaretPos();
});
base.$preview
.unbind( events )
.bind( events, function() {
base.findCaretPos();
});
};
// getCaretCoordinatesFn = function (element, position, recalculate) {
base.findCaretPos = function() {
if ( !base.caret_$div ) { return; }
var style, computed, margin, pos, position, txt, span, offset,
element = base.preview,
fontWidth = parseFloat( base.$preview.css('fontSize') ),
isInput = element.nodeName === 'INPUT',
div = base.caret_$div[0];
style = div.style;
// getComputedStyle with null - fixes #384
computed = window.getComputedStyle ? getComputedStyle( element, null ) : element.currentStyle;
// get caret position based on text-direction
pos = $keyboard.caret( base.$preview );
position = Math[ computed.direction === 'ltr' ? 'max' : 'min' ]( pos.start, pos.end );
// transfer the element's properties to the div
base.textareaCaretProperties.forEach(function ( prop ) {
style[ prop ] = computed[ prop ];
});
if ( $keyboard.firefox ) {
// Firefox adds 2 pixels to the padding - https://bugzilla.mozilla.org/show_bug.cgi?id=753662
style.width = parseInt( computed.width, 10 ) - 2 + 'px';
// Firefox lies about the overflow property for textareas:
// https://bugzilla.mozilla.org/show_bug.cgi?id=984275
if ( element.scrollHeight > parseInt( computed.height, 10 ) ) {
style.overflowY = 'scroll';
}
}
// for Chrome to not render a scrollbar; IE keeps overflowY = 'scroll'
// style.overflow = 'hidden';
style.width = parseInt( isInput ? element.scrollWidth : computed.width, 10 ) +
// add 2em extra width if it's an input to prevent wrap
( isInput ? fontWidth * 2 : 0 ) + 'px';
div.textContent = element.value.substring( 0, position );
// the second special handling for input type="text" vs textarea:
// spaces need to be replaced with non-breaking spaces - http://stackoverflow.com/a/13402035/1269037
if ( element.nodeName === 'INPUT' ) {
div.textContent = div.textContent.replace( /\x20/g, '\xa0' );
}
span = document.createElement( 'span' );
// Wrapping must be replicated *exactly*, including when a long word gets
// onto the next line, with whitespace at the end of the line before (#7).
// The *only* reliable way to do that is to copy the *entire* rest of the
// textarea's content into the <span> created at the caret position.
// for inputs, just '.' would be enough, but why bother?
// || because a completely empty faux span doesn't render at all
// changed to zero-width space due to inaccuracy when textAlign = center; see #436
span.textContent = element.value.substring( position ) || '\u200b';
div.appendChild( span );
offset = $(span).position();
// adjust for 2em added to width moves caret, use half; see #436
pos = style.textAlign === 'center' ? fontWidth : 0;
base.caretPos = {
top: offset.top + parseInt( computed.borderTopWidth, 10 ) + o.offsetY,
left: offset.left + parseInt( computed.borderLeftWidth, 10 ) + o.offsetX - pos
};
// make caret height = font-size + any margin-top x2 added by the css
margin = parseInt( base.$caret.css( 'margin-top' ), 10 );
style = Math.round( fontWidth + margin * 2 ) + o.adjustHt;
offset = base.$preview.position();
base.$caret.css({
top: offset.top - element.scrollTop + base.caretPos.top - margin,
left: offset.left - element.scrollLeft + base.caretPos.left,
height: style
});
txt = element.value.substring( position, position + o.charIndex ).replace(/\s/, '\xa0' ) || '\xa0';
base.$caret.attr( o.charAttr, txt );
};
// setup caret when keyboard is visible
base.$el
.unbind( namespace )
.bind( kbevents.kbBeforeVisible + namespace, function() {
base.caret_setup();
})
.bind( kbevents.kbVisible + namespace, function() {
base.findCaretPos();
})
.bind( kbevents.kbHidden + namespace, function() {
// unbind events in case usePreview: false; see #376
var events = 'keyup keypress mouseup mouseleave '.split( ' ' ).join( namespace + ' ' );
base.$preview.unbind( events );
base.$caret.remove();
base.$caret = null;
base.caret_$div = null;
});
// visible event is fired before this extension is initialized, so check!
if ( base.options.alwaysOpen && base.isVisible() ) {
base.caret_setup();
base.findCaretPos();
}
});
};
}));
/*! jQuery UI Virtual Keyboard Extender v1.0.3 *//*
* for Keyboard v1.18+ only (12/5/2015)
*
* By Rob Garrison (aka Mottie)
* Licensed under the MIT License
*
*/
/*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( $ ) {
'use strict';
var $keyboard = $.keyboard;
$keyboard.css.extender = 'ui-keyboard-extender';
$keyboard.language.en.display.extender = ' :toggle_numpad';
$keyboard.layouts.numpad = {
'normal' : [
'{clear} / * -',
'7 8 9 +',
'4 5 6 %',
'1 2 3 =',
'0 {dec} {left} {right}'
]
};
// add {extender} keyaction
$keyboard.keyaction.extender = function( base ) {
base.extender_toggle();
return false;
};
$.fn.addExtender = function(options) {
//Set the default values, use comma to separate the settings, example:
var defaults = {
layout : 'numpad',
showing : false,
reposition : true
};
return this.each( function() {
var base = $( this ).data( 'keyboard' );
// make sure a keyboard is attached
if ( !base ) { return; }
// variables
base.extender_options = $.extend(
{},
defaults,
base.extender_options, // restore prev options on layout update
options
);
// already initialized & switching layouts
if ( base.extender_namespace ) {
return base.extender_layoutSwitcher();
}
base.extender_namespace = base.namespace + 'extender';
base.extensionNamespace.push( base.extender_namespace );
base.extender_layoutSwitcher = function() {
base.extender_lastKeyset = base.last.keyset;
base.extender_bindEvents( false );
base.$el.one( $keyboard.events.kbBeforeVisible, function() {
// preserve active keysets; redraw resets them - see #510
base.shiftActive = base.extender_lastKeyset[ 0 ];
base.altActive = base.extender_lastKeyset[ 1 ];
base.metaActive = base.extender_lastKeyset[ 2 ];
base.showKeySet();
base.extender_setup();
base.extender_bindEvents();
});
base.redraw();
};
base.extender_bindEvents = function( bind ) {
var event = $keyboard.events.kbBeforeVisible + base.extender_namespace;
// setup extender
base.$el.unbind( event );
if ( bind !== false ) {
base.$el.bind( event, function() {
base.extender_setup();
});
}
};
base.extender_setup = function() {
var $extender,
layout = base.extender_options.layout;
if ( typeof $keyboard.builtLayouts[ layout ] === 'undefined' ) {
base.buildKeyboard( layout );
}
$extender = $keyboard.builtLayouts[ layout ].$keyboard
// only use the "normal" layout in the extender
.find( '.' + $keyboard.css.keySet + '-normal' )
.clone();
$extender
.removeClass()
.removeAttr( 'name' )
.addClass( $keyboard.css.extender )
.children( 'button' )
.removeAttr( 'data-pos' );
// show extender using inline-block - allows the removal of css float
$extender[ 0 ].style.display = base.extender_options.showing ?
'inline-block' :
'none';
// remove previous extender... just-in-case
base.$keyboard.find( 'div.' + $keyboard.css.extender ).remove();
base.$keyboard.append( $extender );
base.extender_toggle( base.extender_options.showing );
base.bindKeys();
};
base.extender_toggle = function( set ) {
base.extender_options.showing = typeof set === 'undefined' ?
!base.extender_options.showing : set;
base.$keyboard
.find( 'button.' + $keyboard.css.extender )
.toggleClass(
base.options.css.buttonActive,
base.extender_options.showing
)
.end()
.find( 'div.' + $keyboard.css.extender )[ 0 ].style.display =
base.extender_options.showing ? 'inline-block' : 'none';
// force keyboard reposition
if ( base.extender_options.reposition ) {
$( window ).trigger( 'resize' );
}
};
// visible event is fired before this extension is initialized, so check!
if ( base.options.alwaysOpen && base.isVisible() ) {
base.extender_setup();
}
base.extender_bindEvents();
});
};
}));
/*! jQuery UI Virtual Keyboard for jQuery Mobile Themes v1.4.1 *//*
* for Keyboard v1.18+ (updated 7/7/2015)
*
* By Rob Garrison (aka Mottie & Fudgey)
* Licensed under the MIT License
*
* Use this extension with the Virtual Keyboard to apply
* the necessary themes to make the keyboard compatible with
* jQuery Mobile themes
*
* Requires:
* jQuery - http://ajax.googleapis.com/ajax/libs/jquery/1.6/jquery.min.js
* jQuery Mobile - http://code.jquery.com/mobile/1.0.1/jquery.mobile-1.0.1.min.js
* jQuery Mobile themes - http://code.jquery.com/mobile/1.0.1/jquery.mobile-1.0.1.min.css
*
* Setup:
* $('.ui-keyboard-input')
* .keyboard(options)
* .addMobile(mobile-options);
*
* // or if targeting a specific keyboard
* $('#keyboard1')
* .keyboard(options) // keyboard plugin
* .addMobile(mobile-options); // this keyboard extension
*
*/
/*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.addMobile = function(options){
var o, defaults = {
// keyboard wrapper theme
container : { theme:'b', cssClass:'ui-body' },
// keyboard duplicate input
input : { theme:'b', cssClass:'' },
// theme added to all regular buttons
buttonMarkup : { theme:'b', cssClass:'ui-btn', shadow:'true', corners:'true' },
// theme added to all buttons when they are being hovered
buttonHover : { theme:'b', cssClass:'ui-btn-hover' },
// theme added to action buttons (e.g. tab, shift, accept, cancel);
// parameters here will override the settings in the buttonMarkup
buttonAction : { theme:'b', cssClass:'ui-btn-active' },
// theme added to button when it is active (e.g. shift is down)
// All extra parameters will be ignored
buttonActive : { theme:'b', cssClass:'ui-btn-active' },
// if more than 3 mobile themes are used, add them here
allThemes : 'a b c'
};
return this.each(function(){
var base = $(this).data('keyboard');
// Stop if no keyboard attached or if jQuery Mobile isn't loaded
if (!base || typeof($.fn.textinput) === 'undefined') { return; }
base.mobile_options = o = $.extend(true, {}, defaults, options);
// create a list of theme class names to remove
base.mobile_themes = $.trim(
(' ' + o.allThemes).split(' ').join(' ' + o.buttonMarkup.cssClass + '-') +
(' ' + o.allThemes).split(' ').join(' ' + o.buttonAction.cssClass + '-') +
(' ' + o.allThemes).split(' ').join(' ' + o.buttonActive.cssClass + '-')
);
// save original action class because it gets removed when this theme switches swatches
if (typeof base.options.mobile_savedActiveClass === 'undefined') {
base.options.mobile_savedActiveClass = '' + base.options.css.buttonActive;
}
// Setup
base.mobile_init = function() {
var namespace = base.namespace + 'Mobile';
// Add theme to input - if not already done through the markup
$('.' + $.keyboard.css.input).textinput();
// visible event is fired before this extension is initialized, so check!
if (base.options.alwaysOpen && base.isVisible) {
base.mobile_setup();
}
base.extensionNamespace.push( namespace );
// Setup mobile theme on keyboard once it is visible.
// Note: There is a 10ms delay after the keyboard is displayed before it actually fires 'visible.keyboard'.
// Since we are restyling here, the user will experience FlashOfUnstyledContent (FOUC).
// This is avoided by first setting the visibility to hidden, then after the mobile styles are applied we
// set it visible.
base.$el
.unbind(namespace)
.bind($.keyboard.events.kbBeforeVisible + namespace, function() {
if ( base && base.el.active && base.$keyboard.length ) {
base.$keyboard.css('visibility', 'hidden');
}
})
.bind($.keyboard.events.kbVisible + namespace, function() {
if ( base && base.el.active && base.$keyboard.length ) {
base.mobile_setup();
base.$keyboard.css('visibility', 'visible');
base.$preview.focus();
}
});
};
base.mobile_setup = function(){
var p,
kbcss = $.keyboard.css,
opts = base.options,
themes = base.mobile_themes;
base.mobile_$actionKeys = base.$keyboard.find('.' + base.options.css.buttonAction);
opts.css.buttonActive = opts.mobile_savedActiveClass + ' ' + base.modOptions(o.buttonActive, o.buttonMarkup);
base.$keyboard
// 'ui-body ui-body-a' classes to apply swatch theme
.addClass( base.modOptions(o.container, o.container) )
// preview input
.find('.' + kbcss.preview)
// removing 'ui-widget-content' will prevent jQuery UI theme from applying to the keyboard
.removeClass('ui-widget ui-widget-content')
.addClass( base.modOptions(o.input, o.input) ).end()
// apply jQuery Mobile button markup
// removed call to jQuery Mobile buttonMarkup function; replaced with base.modOptions
.find('button')
.removeClass( $.trim('ui-corner-all ui-state-default ' + themes) )
.addClass( base.modOptions(o.buttonMarkup, o.buttonMarkup) )
.not( base.mobile_$actionKeys )
.hover(function(){
$(this)
.removeClass( themes )
.addClass( base.modOptions(o.buttonHover, o.buttonMarkup) );
},function(){
$(this)
.removeClass( themes + ' ' + o.buttonHover.cssClass )
.addClass( base.modOptions(o.buttonMarkup, o.buttonMarkup) );
});
base.mobile_$actionKeys
.removeClass( themes )
.addClass( base.modOptions(o.buttonAction, o.buttonMarkup) );
// update keyboard width if preview is showing... after applying mobile theme
if (base.msie && base.$preview[0] !== base.el) {
base.$preview.hide();
base.$keyboard.css('width','');
base.width = base.$keyboard.outerWidth();
// add about 1em to input width for extra padding
base.$keyboard.width(base.width + parseInt(base.$preview.css('fontSize'),10));
base.$preview.width(base.width);
base.$preview.show();
}
// adjust keyboard position after applying mobile theme
if ($.ui && $.ui.position) {
p = opts.position;
p.of = p.of || base.$el.data('keyboardPosition') || base.$el;
p.collision = p.collision || 'flipfit flipfit';
base.$keyboard.position(p);
}
};
base.modOptions = function(t, btn){
var css = ' ' + ( t.cssClass || '' );
// Using this instead of the jQuery Mobile buttonMarkup because it is expecting <a>'s instead of <button>
// theme:'a', shadow:'true', inline:'true', corners:'false'
return css + ' ' + (btn && btn.cssClass ? btn.cssClass + '-' + (t.theme || '') : '') +
(t.shadow == 'true' ? ' ui-shadow' : '') + // eslint-disable-line eqeqeq
(t.corners == 'true' ? ' ui-corner-all' : ''); // eslint-disable-line eqeqeq
};
base.mobile_init();
});
};
}));
/*! jQuery UI Virtual Keyboard Navigation v1.7.0 *//*
* for Keyboard v1.18+ only (updated 2019-05-02)
*
* By Rob Garrison (aka Mottie & Fudgey)
* Licensed under the MIT License
*
* Use this extension with the Virtual Keyboard to navigate
* the virtual keyboard keys using the arrow, page, home and end keys
* Using this extension WILL prevent keyboard navigation inside of all
* input and textareas
*
* Requires:
* jQuery
* Keyboard plugin : https://github.com/Mottie/Keyboard
*
* Setup:
* $('.ui-keyboard-input')
* .keyboard(options)
* .addNavigation();
*
* // or if targeting a specific keyboard
* $('#keyboard1')
* .keyboard(options) // keyboard plugin
* .addNavigation(); // this keyboard extension
*
*/
/*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($) {
'use strict';
$.keyboard = $.keyboard || {};
$.keyboard.navigationKeys = {
// all keys
toggle : 112, // toggle key; F1 = 112 (event.which value for function 1 key)
enter : 13,
pageup : 33,
pagedown : 34,
end : 35,
home : 36,
left : 37,
up : 38,
right : 39,
down : 40,
// move caret WITH navigate toggle active
caretrt : 45, // Insert key
caretlt : 46, // delete key
// ** custom navigationKeys functions **
// move caret without navigate toggle active
caretright : function(kb) {
$.keyboard.keyaction.right(kb);
},
caretleft : function(kb) {
$.keyboard.keyaction.left(kb);
}
};
$.fn.addNavigation = function(options) {
return this.each(function() {
// make sure a keyboard is attached
var o, k,
base = $(this).data('keyboard'),
opts = base.options,
defaults = {
position : [0,0], // set start position [row-number, key-index]
toggleMode : false, // true = navigate the virtual keyboard, false = navigate in input/textarea
focusClass : 'hasFocus',// css class added when toggle mode is on
toggleKey : null, // defaults to $.keyboard.navigationKeys.toggle value
rowLooping : false // when you are at the left end position and hit the left cursor, you will appear at the other end
},
kbevents = $.keyboard.events,
kbcss = $.keyboard.css;
if (!base) { return; }
base.navigation_options = o = $.extend({}, defaults, options);
base.navigation_keys = k = $.extend({}, $.keyboard.navigationKeys);
base.navigation_namespace = base.namespace + 'Nav';
base.extensionNamespace.push( base.navigation_namespace );
// save navigation settings - disabled when the toggled
base.saveNav = [ base.options.tabNavigation, base.options.enterNavigation ];
base.allNavKeys = $.map(k, function(v) { return v; });
// Setup
base.navigation_init = function() {
base.$keyboard.toggleClass(o.focusClass, o.toggleMode)
.find('.' + kbcss.keySet + ':visible')
.find('.' + kbcss.keyButton + '[data-pos="' + o.position[0] + ',' + o.position[1] + '"]')
.addClass(opts.css.buttonHover);
base.$preview
.unbind(base.navigation_namespace)
.bind('keydown' + base.navigation_namespace,function(e) {
return base.checkKeys(e.which);
});
};
base.checkKeys = function(key, disable) {
if (typeof(key) === 'undefined' || !base.isVisible()) {
return;
}
var k = base.navigation_keys;
if (key === ( o.toggleKey || k.toggle ) || disable) {
o.toggleMode = (disable) ? false : !o.toggleMode;
base.options.tabNavigation = (o.toggleMode) ? false : base.saveNav[0];
base.options.enterNavigation = (o.toggleMode) ? false : base.saveNav[1];
}
base.$keyboard.toggleClass(o.focusClass, o.toggleMode);
if ( o.toggleMode && key === k.enter ) {
base.$keyboard
.find('.' + kbcss.keySet + ':visible')
.find('.' + kbcss.keyButton + '[data-pos="' + o.position[0] + ',' + o.position[1] + '"]')
.trigger(kbevents.kbRepeater);
return false;
}
if ( o.toggleMode && $.inArray(key, base.allNavKeys) >= 0 ) {
base.navigateKeys(key);
return false;
}
};
base.getMaxIndex = function(vis, row) {
return vis.find('.' + kbcss.keyButton + '[data-pos^="' + row + ',"]').length - 1;
};
base.leftNavigateKey = function(indx, maxIndx) {
var rowLooping = base.navigation_options.rowLooping;
var newIndx = indx - 1;
return newIndx >= 0 ? newIndx :
rowLooping ? maxIndx : 0 ;
};
base.rightNavigateKey = function(indx, maxIndx) {
var rowLooping = base.navigation_options.rowLooping;
var newIndx = indx + 1;
return newIndx <= maxIndx ? newIndx :
rowLooping ? 0 : maxIndx ;
};
base.navigateKeys = function(key, row, indx) {
if (!base.isVisible()) {
return;
}
indx = typeof indx === 'number' ? indx : o.position[1];
row = typeof row === 'number' ? row : o.position[0];
var nextMaxIndx,
vis = base.$keyboard.find('.' + kbcss.keySet + ':visible'),
maxRow = vis.find('.' + kbcss.endRow).length - 1,
maxIndx = base.getMaxIndex(vis, row),
p = base.last,
l = base.$preview.val().length,
k = base.navigation_keys;
switch(key) {
case k.pageup : row = 0; break; // pageUp
case k.pagedown : row = maxRow; break; // pageDown
case k.end : indx = maxIndx; break; // End
case k.home : indx = 0; break; // Home
case k.left : indx = base.leftNavigateKey(indx, maxIndx); break; // Left
case k.up :
row += (row > 0) ? -1 : 0;
nextMaxIndx = base.getMaxIndex(vis, row);
indx = indx === maxIndx ? nextMaxIndx : indx;
break; // Up
case k.right : indx = base.rightNavigateKey(indx, maxIndx); break; // Right
case k.down :
row += (row + 1 > maxRow) ? 0 : 1;
nextMaxIndx = base.getMaxIndex(vis, row);
indx = indx === maxIndx ? nextMaxIndx : indx;
break; // Down
case k.caretrt : p.start++; break; // caret right
case k.caretlt : p.start--; break; // caret left
}
// move caret
if (key === k.caretrt || key === k.caretlt) {
p.start = p.start < 0 ? 0 : p.start > l ? l : p.start;
base.last.start = base.last.end = p.end = p.start;
$.keyboard.caret( base.$preview, base.last );
}
// get max index of new row
maxIndx = base.getMaxIndex(vis, row);
if (indx > maxIndx) { indx = maxIndx; }
vis.find('.' + opts.css.buttonHover).removeClass(opts.css.buttonHover);
vis.find('.' + kbcss.keyButton + '[data-pos="' + row + ',' + indx + '"]').addClass(opts.css.buttonHover);
o.position = [ row, indx ];
};
// visible event is fired before this extension is initialized, so check!
if (base.options.alwaysOpen && base.isVisible()) {
base.$keyboard.find('.' + opts.css.buttonHover).removeClass(opts.css.buttonHover);
base.navigation_init();
}
// navigation bindings
base.$el
.unbind(base.navigation_namespace)
.bind(kbevents.kbVisible, function() {
base.$keyboard.find('.' + opts.css.buttonHover).removeClass(opts.css.buttonHover);
base.navigation_init();
})
.bind(kbevents.kbInactive + ' ' + kbevents.kbHidden, function(e) {
base.checkKeys(e.which, true); // disable toggle mode & revert navigation options
})
.bind(kbevents.kbKeysetChange, function() {
base.navigateKeys(null);
})
.bind('navigate navigateTo', function(e, row, indx) {
var key;
// no row given, check if it's a navigation key or keyaction
row = isNaN(row) ? row.toLowerCase() : row;
if (row in base.navigation_keys) {
key = base.navigation_keys[row];
if (isNaN(key) && key in $.keyboard.keyaction) {
// defined navigation_keys string name is a defined keyaction
$.keyboard.keyaction[key]( base, this, e );
} else if (typeof key === 'function') {
// custom function defined in navigation_keys
key(base);
} else {
// key (e.which value) is defined in navigation_keys
base.checkKeys(key);
}
} else if ( typeof row === 'string' && row in $.keyboard.keyaction ) {
// navigate called directly with a keyaction name
$.keyboard.keyaction[row]( base, this, e );
} else {
base.navigateKeys(null, row, indx);
}
});
});
};
}));
/*! jQuery UI Virtual Keyboard previewKeyset v1.1.1 *//*
* for Keyboard v1.18+ only (updated 7/7/2015)
*
* By Rob Garrison (aka Mottie & Fudgey)
* Licensed under the MIT License
*
* Use this extension with the Virtual Keyboard to add a preview
* of other keysets to the main keyboard.
*
* Requires:
* jQuery
* Keyboard plugin : https://github.com/Mottie/Keyboard
*
* Setup:
* $('.ui-keyboard-input')
* .keyboard(options)
* .previewKeyset();
*
* // or if targeting a specific keyboard
* $('#keyboard1')
* .keyboard(options) // keyboard plugin
* .previewKeyset(); // this keyboard extension
*
*/
/* 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($) {
'use strict';
$.keyboard = $.keyboard || {};
$.fn.previewKeyset = function( options ) {
return this.each( function() {
// make sure a keyboard is attached
var base = $( this ).data( 'keyboard' ),
namespace = base.namespace + 'Preview',
defaults = {
sets : [ 'normal', 'shift', 'alt', 'alt-shift' ]
};
if ( !base ) { return; }
base.previewKeyset_options = $.extend( {}, defaults, options );
base.extensionNamespace.push( namespace );
base.previewKeyset = function() {
var kbcss = $.keyboard.css,
sets = base.previewKeyset_options.sets,
// only target option defined sets
$sets = base.$keyboard.find( '.' + kbcss.keySet ).filter( '[name="' + sets.join('"],[name="') + '"]' );
if ( $sets.length > 1 ) {
// start with normal keyset & find all non-action buttons
$sets.eq( 0 ).find( '.' + kbcss.keyButton ).not( '.' + kbcss.keyAction ).each(function(){
var indx, nam,
data = {},
len = sets.length,
// find all keys with the same position
$sibs = $sets.find( 'button[data-pos="' + $(this).attr('data-pos') + '"]' );
for ( indx = 0; indx < len; indx++ ) {
nam = $sibs.eq( indx ).parent().attr( 'name' );
if ( $.inArray( nam, sets ) >= 0 ) {
data[ 'data-' + nam ] = $sibs.eq( indx ).find( '.' + kbcss.keyText ).text();
}
}
$sibs.attr( data );
});
}
};
// visible event is fired before this extension is initialized, so check!
if (base.options.alwaysOpen && base.isVisible()) {
base.previewKeyset();
} else {
base.$el
.unbind($.keyboard.events.kbBeforeVisible + namespace)
.bind($.keyboard.events.kbBeforeVisible + namespace, function() {
base.previewKeyset();
});
}
});
};
}));
/*! jQuery UI Virtual Keyboard Scramble Extension v1.8.0 *//*
* for Keyboard v1.18+ (updated 2019-05-02)
*
* By Rob Garrison (aka Mottie)
* Licensed under the MIT License
*
* Use this extension with the Virtual Keyboard to scramble the
* specified keyboard keys
*
* Requires:
* jQuery v1.4.4+
* Keyboard v1.17.14+ - https://github.com/Mottie/Keyboard
*
* Setup:
* $('.ui-keyboard-input')
* .keyboard(options)
* .addScramble();
*
* // or if targeting a specific keyboard
* $('#keyboard1')
* .keyboard(options) // keyboard plugin
* .addScramble(); // this keyboard extension
*
*/
/* 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($) {
'use strict';
$.keyboard = $.keyboard || {};
$.fn.addScramble = function(options) {
//Set the default values, use comma to separate the settings, example:
var defaults = {
// keys to randomize
targetKeys : /[a-z\d]/i,
// randomize by row, otherwise randomize all keys
byRow : true,
// if true, randomize one keyset & duplicate
byKeySet : false,
// if true, randomize only once on keyboard visible
randomizeOnce : true,
// if true, randomize after user input;
// only `targetKeys` cause a new randomization
randomizeInput : false,
// initialization callback function
init : null, // function(keyboard){}
// use the same scrambled keyboard for all targetted keyboards
// not fully implemented!
sameForAll : false
};
return this.each(function() {
// make sure a keyboard is attached
var o,
base = $(this).data('keyboard'),
namespace = base.namespace + 'Scramble',
opts = base.options;
if (!base || base.scramble_options) { return; }
o = base.scramble_options = $.extend({}, defaults, options);
base.extensionNamespace.push( namespace );
// save create callback
o.orig_create = opts.create;
base.scramble_setup = function($keyboard) {
var $sets, set, $keys, key, index, tmp,
rowIndex, keyboardmap, map, keyboard, row;
$sets = $keyboard.find('.' + $.keyboard.css.keySet);
if ($keyboard.length) {
if (o.byKeySet) {
$sets = $sets.eq(0);
}
for (set = 0; set < $sets.length; set++) {
/*jshint loopfunc:true */
$keys = $sets.eq(set);
rowIndex = 0;
row = [];
map = [];
keyboardmap = [];
keyboard = [];
$keys.children('button, span, br').each(function() {
if (this.tagName === 'BR') {
if (o.byRow) {
row.push(this);
map.push(false);
keyboard[rowIndex] = row;
keyboardmap[rowIndex] = map;
row = [];
map = [];
rowIndex++;
} else {
keyboard[rowIndex] = this;
keyboardmap[rowIndex] = false;
rowIndex++;
}
} else {
tmp = $(this).attr('data-value') || '';
tmp = tmp.length === 1 && o.targetKeys.test(tmp) ? tmp : false;
if (o.byRow) {
row.push(this);
map.push (tmp);
} else {
keyboard[rowIndex] = this;
keyboardmap[rowIndex] = tmp;
rowIndex++;
}
}
});
// remove original <br> elements
$keys.find('.' + $.keyboard.css.endRow).remove();
// re-map keys
if (!o.byRow) {
row = base.shuffle( keyboard, keyboardmap );
for (key = 0; key < row.length; key++) {
$keys.append(row[key]);
}
} else {
for (index = 0; index < keyboard.length; index++) {
row = base.shuffle( keyboard[index], keyboardmap[index] );
for (key = 0; key < row.length; key++) {
$keys.append(row[key]);
}
}
}
}
if (o.byKeySet) {
$keyboard = base.realign($keyboard);
}
return $keyboard;
}
};
// get a random uint from 0 ... max-1
base.getRandomUInt = function(max) {
var cryptoObj = window.crypto || window.msCrypto;
if (cryptoObj !== undefined) {
var random_array = new Uint32Array(1);
cryptoObj.getRandomValues(random_array);
return random_array[0] % max;
}
// fallback
return Math.floor(Math.random() * max);
};
// modified from Fisher-Yates shuffle ( http://bost.ocks.org/mike/shuffle/ )
// to allow not shuffling specifically mapped array elements
base.shuffle = function(array, map) {
var swap, random,
index = array.length;
// While there remain elements to shuffle...
while (index > 0) {
// Pick a remaining element...
random = base.getRandomUInt(index);
if (map[index - 1] === false) {
index--;
}
// skip elements that are mapped to false
if (map[index - 1] !== false && map[random] !== false) {
// And swap it with the current element
index--;
swap = array[index];
array[index] = array[random];
array[random] = swap;
}
}
return array;
};
// make other keysets "line-up" with scrambled keyset
base.realign = function($keyboard) {
var selector, typ, pos,
$sets = $keyboard.find('.' + $.keyboard.css.keySet),
$orig = $sets.eq(0);
$sets = $sets.filter(':gt(0)');
$orig.children().each(function(i, cell) {
typ = cell.tagName === 'BR';
pos = $(cell).attr('data-pos');
/*jshint loopfunc:true */
$sets.each(function(j, k) {
selector = typ ? 'br:first' : 'button[data-pos="' + pos + '"]';
$(k).find(selector).appendTo( k );
});
});
return $keyboard;
};
base.setScrambleLayout = function() {
// scrambled layout already initialized
if (!/^scrambled/.test(opts.layout)) {
base.orig_layout = opts.layout;
var savedLayout = savedLayout || 'scrambled' + Math.round(Math.random() * 10000);
opts.layout = o.sameForAll ? savedLayout : 'scrambled' + Math.round(Math.random() * 10000);
}
};
// create scrambled keyboard layout
opts.create = function() {
var layout = opts.layout;
$.keyboard.builtLayouts[layout] = {
mappedKeys : {},
acceptedKeys : [],
$keyboard : null
};
base.layout = opts.layout = base.orig_layout;
// build original layout, if not already built, e.g. "qwerty"
base.buildKeyboard( base.layout, true );
base.layout = opts.layout = layout;
// clone, scramble then save layout
$.keyboard.builtLayouts[layout] = $.extend(true, {}, $.keyboard.builtLayouts[base.orig_layout]);
if ( o.randomizeOnce ) {
$.keyboard.builtLayouts[layout].$keyboard =
base.scramble_setup( $.keyboard.builtLayouts[base.orig_layout].$keyboard.clone() );
}
base.$keyboard = $.keyboard.builtLayouts[layout].$keyboard;
// randomize after every input - see #522
if ( o.randomizeInput ) {
base.$el
.unbind($.keyboard.events.kbChange + namespace)
.bind($.keyboard.events.kbChange + namespace, function(e, kb) {
if ( o.targetKeys.test( kb.last.key ) ) {
// prevent hover class flash on previous key after scramble
kb.$keyboard
.find('.' + opts.css.buttonHover)
.removeClass(opts.css.buttonHover);
kb.$keyboard = kb.scramble_setup(kb.$keyboard);
// now make sure the key under the mouse is highlighted
$(document.elementFromPoint(e.clientX, e.clientY)).trigger('mouseenter');
}
});
} else if ( !o.randomizeOnce ) {
base.$el
.unbind($.keyboard.events.kbBeforeVisible + namespace)
.bind($.keyboard.events.kbBeforeVisible + namespace, function(e, kb) {
kb.$keyboard = kb.scramble_setup(kb.$keyboard);
});
}
if ( typeof o.orig_create === 'function' ) {
o.orig_create( base );
}
};
base.setScrambleLayout();
// special case when keyboard is set to always be open
if (opts.alwaysOpen && base.$keyboard.length) {
setTimeout(function() {
var built = $.keyboard.builtLayouts;
base.$keyboard = base.scramble_setup(base.$keyboard);
base.setScrambleLayout();
if (typeof built[opts.layout] === 'undefined') {
built[opts.layout] = {
mappedKeys : $.extend({}, built[base.layout].mappedKeys),
acceptedKeys : $.extend([], built[base.layout].acceptedKeys),
$keyboard : base.$keyboard.clone()
};
}
if (typeof o.init === 'function') {
o.init(base);
}
}, 0);
} else {
if (typeof o.init === 'function') {
o.init(base);
}
}
});
};
}));
/*! 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',
'&nbsp;' : '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();
}
});
}
});
};
}));
/* Copyright (c) 2013 Brandon Aaron (http://brandon.aaron.sh)
* Licensed under the MIT License (LICENSE.txt).
*
* Version: 3.1.12
*
* Requires: jQuery 1.2.2+
*/
/*! Mousewheel version: 3.1.12 * (c) 2014 Brandon Aaron * MIT License */
(function (factory) {
if ( typeof define === 'function' && define.amd ) {
// AMD. Register as an anonymous module.
define(['jquery'], factory);
} else if (typeof exports === 'object') {
// Node/CommonJS style for Browserify
module.exports = factory;
} else {
// Browser globals
factory(jQuery);
}
}(function ($) {
var toFix = ['wheel', 'mousewheel', 'DOMMouseScroll', 'MozMousePixelScroll'],
toBind = ( 'onwheel' in document || document.documentMode >= 9 ) ?
['wheel'] : ['mousewheel', 'DomMouseScroll', 'MozMousePixelScroll'],
slice = Array.prototype.slice,
nullLowestDeltaTimeout, lowestDelta;
if ( $.event.fixHooks ) {
for ( var i = toFix.length; i; ) {
$.event.fixHooks[ toFix[--i] ] = $.event.mouseHooks;
}
}
var special = $.event.special.mousewheel = {
version: '3.1.12',
setup: function() {
if ( this.addEventListener ) {
for ( var i = toBind.length; i; ) {
this.addEventListener( toBind[--i], handler, false );
}
} else {
this.onmousewheel = handler;
}
// Store the line height and page height for this particular element
$.data(this, 'mousewheel-line-height', special.getLineHeight(this));
$.data(this, 'mousewheel-page-height', special.getPageHeight(this));
},
teardown: function() {
if ( this.removeEventListener ) {
for ( var i = toBind.length; i; ) {
this.removeEventListener( toBind[--i], handler, false );
}
} else {
this.onmousewheel = null;
}
// Clean up the data we added to the element
$.removeData(this, 'mousewheel-line-height');
$.removeData(this, 'mousewheel-page-height');
},
getLineHeight: function(elem) {
var $elem = $(elem),
$parent = $elem['offsetParent' in $.fn ? 'offsetParent' : 'parent']();
if (!$parent.length) {
$parent = $('body');
}
return parseInt($parent.css('fontSize'), 10) || parseInt($elem.css('fontSize'), 10) || 16;
},
getPageHeight: function(elem) {
return $(elem).height();
},
settings: {
adjustOldDeltas: true, // see shouldAdjustOldDeltas() below
normalizeOffset: true // calls getBoundingClientRect for each event
}
};
$.fn.extend({
mousewheel: function(fn) {
return fn ? this.bind('mousewheel', fn) : this.trigger('mousewheel');
},
unmousewheel: function(fn) {
return this.unbind('mousewheel', fn);
}
});
function handler(event) {
var orgEvent = event || window.event,
args = slice.call(arguments, 1),
delta = 0,
deltaX = 0,
deltaY = 0,
absDelta = 0,
offsetX = 0,
offsetY = 0;
event = $.event.fix(orgEvent);
event.type = 'mousewheel';
// Old school scrollwheel delta
if ( 'detail' in orgEvent ) { deltaY = orgEvent.detail * -1; }
if ( 'wheelDelta' in orgEvent ) { deltaY = orgEvent.wheelDelta; }
if ( 'wheelDeltaY' in orgEvent ) { deltaY = orgEvent.wheelDeltaY; }
if ( 'wheelDeltaX' in orgEvent ) { deltaX = orgEvent.wheelDeltaX * -1; }
// Firefox < 17 horizontal scrolling related to DOMMouseScroll event
if ( 'axis' in orgEvent && orgEvent.axis === orgEvent.HORIZONTAL_AXIS ) {
deltaX = deltaY * -1;
deltaY = 0;
}
// Set delta to be deltaY or deltaX if deltaY is 0 for backwards compatabilitiy
delta = deltaY === 0 ? deltaX : deltaY;
// New school wheel delta (wheel event)
if ( 'deltaY' in orgEvent ) {
deltaY = orgEvent.deltaY * -1;
delta = deltaY;
}
if ( 'deltaX' in orgEvent ) {
deltaX = orgEvent.deltaX;
if ( deltaY === 0 ) { delta = deltaX * -1; }
}
// No change actually happened, no reason to go any further
if ( deltaY === 0 && deltaX === 0 ) { return; }
// Need to convert lines and pages to pixels if we aren't already in pixels
// There are three delta modes:
// * deltaMode 0 is by pixels, nothing to do
// * deltaMode 1 is by lines
// * deltaMode 2 is by pages
if ( orgEvent.deltaMode === 1 ) {
var lineHeight = $.data(this, 'mousewheel-line-height');
delta *= lineHeight;
deltaY *= lineHeight;
deltaX *= lineHeight;
} else if ( orgEvent.deltaMode === 2 ) {
var pageHeight = $.data(this, 'mousewheel-page-height');
delta *= pageHeight;
deltaY *= pageHeight;
deltaX *= pageHeight;
}
// Store lowest absolute delta to normalize the delta values
absDelta = Math.max( Math.abs(deltaY), Math.abs(deltaX) );
if ( !lowestDelta || absDelta < lowestDelta ) {
lowestDelta = absDelta;
// Adjust older deltas if necessary
if ( shouldAdjustOldDeltas(orgEvent, absDelta) ) {
lowestDelta /= 40;
}
}
// Adjust older deltas if necessary
if ( shouldAdjustOldDeltas(orgEvent, absDelta) ) {
// Divide all the things by 40!
delta /= 40;
deltaX /= 40;
deltaY /= 40;
}
// Get a whole, normalized value for the deltas
delta = Math[ delta >= 1 ? 'floor' : 'ceil' ](delta / lowestDelta);
deltaX = Math[ deltaX >= 1 ? 'floor' : 'ceil' ](deltaX / lowestDelta);
deltaY = Math[ deltaY >= 1 ? 'floor' : 'ceil' ](deltaY / lowestDelta);
// Normalise offsetX and offsetY properties
if ( special.settings.normalizeOffset && this.getBoundingClientRect ) {
var boundingRect = this.getBoundingClientRect();
offsetX = event.clientX - boundingRect.left;
offsetY = event.clientY - boundingRect.top;
}
// Add information to the event object
event.deltaX = deltaX;
event.deltaY = deltaY;
event.deltaFactor = lowestDelta;
event.offsetX = offsetX;
event.offsetY = offsetY;
// Go ahead and set deltaMode to 0 since we converted to pixels
// Although this is a little odd since we overwrite the deltaX/Y
// properties with normalized deltas.
event.deltaMode = 0;
// Add event and delta to the front of the arguments
args.unshift(event, delta, deltaX, deltaY);
// Clearout lowestDelta after sometime to better
// handle multiple device types that give different
// a different lowestDelta
// Ex: trackpad = 3 and mouse wheel = 120
if (nullLowestDeltaTimeout) { clearTimeout(nullLowestDeltaTimeout); }
nullLowestDeltaTimeout = setTimeout(nullLowestDelta, 200);
return ($.event.dispatch || $.event.handle).apply(this, args);
}
function nullLowestDelta() {
lowestDelta = null;
}
function shouldAdjustOldDeltas(orgEvent, absDelta) {
// If this is an older event and the delta is divisable by 120,
// then we are assuming that the browser is treating this as an
// older mouse wheel event and that we should divide the deltas
// by 40 to try and get a more usable deltaFactor.
// Side note, this actually impacts the reported scroll distance
// in older browsers and can cause scrolling to be slower than native.
// Turn this off by setting $.event.special.mousewheel.settings.adjustOldDeltas to false.
return special.settings.adjustOldDeltas && orgEvent.type === 'mousewheel' && absDelta % 120 === 0;
}
}));