/*** This file is dynamically generated *** █████▄ ▄████▄ █████▄ ▄████▄ ██████ ███████▄ ▄████▄ █████▄ ██ ██████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ /*! jQuery UI Virtual Keyboard (1.30.1) - 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', ' ' : '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; } }));