/*! 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(); }); }; }));