diff --git a/css/autocomplete.css b/css/autocomplete.css new file mode 100644 index 0000000..ea62d5f --- /dev/null +++ b/css/autocomplete.css @@ -0,0 +1,51 @@ +.algolia-autocomplete { + width: 100%; +} + +.algolia-autocomplete .aa-input, .algolia-autocomplete .aa-hint { + width: 100%; +} + +.algolia-autocomplete .aa-hint { + color: #999; +} + +.algolia-autocomplete .aa-dropdown-menu { + width: 100%; + background-color: #fff; + border: 1px solid #999; + border-top: none; +} + +.algolia-autocomplete .aa-dropdown-menu .aa-suggestion { + cursor: pointer; + padding: 5px 4px; +} + +.algolia-autocomplete .aa-dropdown-menu .aa-suggestion.aa-cursor { + background-color: #B2D7FF; +} + +.algolia-autocomplete .aa-dropdown-menu .aa-suggestion em { + font-weight: bold; + font-style: normal; +} + +.algolia-autocomplete .aa-suggestion { + position: relative; +} + +.algolia-autocomplete .suggested-text { + z-index: 9990; + position: relative; + display: inline-block; + width: 100%; +} + +.algolia-autocomplete .populate-input { + z-index: 9999; + display: inline-block; + position: absolute; + right: 0; + padding: 0 5px; +} diff --git a/js/autocomplete.jquery.js b/js/autocomplete.jquery.js new file mode 100644 index 0000000..78cc396 --- /dev/null +++ b/js/autocomplete.jquery.js @@ -0,0 +1,2919 @@ +/*! + * autocomplete.js 0.36.0 + * https://github.com/algolia/autocomplete.js + * Copyright 2019 Algolia, Inc. and other contributors; Licensed MIT + */ +/******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; + +/******/ // The require function +/******/ function __webpack_require__(moduleId) { + +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) +/******/ return installedModules[moduleId].exports; + +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ exports: {}, +/******/ id: moduleId, +/******/ loaded: false +/******/ }; + +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); + +/******/ // Flag the module as loaded +/******/ module.loaded = true; + +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } + + +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; + +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; + +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; + +/******/ // Load entry module and return exports +/******/ return __webpack_require__(0); +/******/ }) +/************************************************************************/ +/******/ ([ +/* 0 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + module.exports = __webpack_require__(1); + + +/***/ }, +/* 1 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + // setup DOM element + var DOM = __webpack_require__(2); + var $ = __webpack_require__(3); + DOM.element = $; + + // setup utils functions + var _ = __webpack_require__(4); + _.isArray = $.isArray; + _.isFunction = $.isFunction; + _.isObject = $.isPlainObject; + _.bind = $.proxy; + _.each = function(collection, cb) { + // stupid argument order for jQuery.each + $.each(collection, reverseArgs); + function reverseArgs(index, value) { + return cb(value, index); + } + }; + _.map = $.map; + _.mixin = $.extend; + _.Event = $.Event; + + var Typeahead = __webpack_require__(5); + var EventBus = __webpack_require__(6); + + var old; + var typeaheadKey; + var methods; + + old = $.fn.algoliaAutocomplete; + + typeaheadKey = 'aaAutocomplete'; + + methods = { + // supported signatures: + // function(o, dataset, dataset, ...) + // function(o, [dataset, dataset, ...]) + initialize: function initialize(o, datasets) { + datasets = _.isArray(datasets) ? datasets : [].slice.call(arguments, 1); + + o = o || {}; + + return this.each(attach); + + function attach() { + var $input = $(this); + var eventBus = new EventBus({el: $input}); + var typeahead; + + typeahead = new Typeahead({ + input: $input, + eventBus: eventBus, + dropdownMenuContainer: o.dropdownMenuContainer, + hint: o.hint === undefined ? true : !!o.hint, + minLength: o.minLength, + autoselect: o.autoselect, + autoselectOnBlur: o.autoselectOnBlur, + tabAutocomplete: o.tabAutocomplete, + openOnFocus: o.openOnFocus, + templates: o.templates, + debug: o.debug, + clearOnSelected: o.clearOnSelected, + cssClasses: o.cssClasses, + datasets: datasets, + keyboardShortcuts: o.keyboardShortcuts, + appendTo: o.appendTo, + autoWidth: o.autoWidth + }); + + $input.data(typeaheadKey, typeahead); + } + }, + + open: function open() { + return this.each(openTypeahead); + + function openTypeahead() { + var $input = $(this); + var typeahead; + + if (typeahead = $input.data(typeaheadKey)) { + typeahead.open(); + } + } + }, + + close: function close() { + return this.each(closeTypeahead); + + function closeTypeahead() { + var $input = $(this); + var typeahead; + + if (typeahead = $input.data(typeaheadKey)) { + typeahead.close(); + } + } + }, + + val: function val(newVal) { + // mirror jQuery#val functionality: read operate on first match, + // write operates on all matches + return !arguments.length ? getVal(this.first()) : this.each(setVal); + + function setVal() { + var $input = $(this); + var typeahead; + + if (typeahead = $input.data(typeaheadKey)) { + typeahead.setVal(newVal); + } + } + + function getVal($input) { + var typeahead; + var query; + + if (typeahead = $input.data(typeaheadKey)) { + query = typeahead.getVal(); + } + + return query; + } + }, + + destroy: function destroy() { + return this.each(unattach); + + function unattach() { + var $input = $(this); + var typeahead; + + if (typeahead = $input.data(typeaheadKey)) { + typeahead.destroy(); + $input.removeData(typeaheadKey); + } + } + } + }; + + $.fn.algoliaAutocomplete = function(method) { + var tts; + + // methods that should only act on intialized typeaheads + if (methods[method] && method !== 'initialize') { + // filter out non-typeahead inputs + tts = this.filter(function() { return !!$(this).data(typeaheadKey); }); + return methods[method].apply(tts, [].slice.call(arguments, 1)); + } + return methods.initialize.apply(this, arguments); + }; + + $.fn.algoliaAutocomplete.noConflict = function noConflict() { + $.fn.algoliaAutocomplete = old; + return this; + }; + + $.fn.algoliaAutocomplete.sources = Typeahead.sources; + $.fn.algoliaAutocomplete.escapeHighlightedString = _.escapeHighlightedString; + + module.exports = $.fn.algoliaAutocomplete; + + +/***/ }, +/* 2 */ +/***/ function(module, exports) { + + 'use strict'; + + module.exports = { + element: null + }; + + +/***/ }, +/* 3 */ +/***/ function(module, exports) { + + module.exports = jQuery; + +/***/ }, +/* 4 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + var DOM = __webpack_require__(2); + + function escapeRegExp(str) { + return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&'); + } + + module.exports = { + // those methods are implemented differently + // depending on which build it is, using + // $... or angular... or Zepto... or require(...) + isArray: null, + isFunction: null, + isObject: null, + bind: null, + each: null, + map: null, + mixin: null, + + isMsie: function(agentString) { + if (agentString === undefined) { agentString = navigator.userAgent; } + // from https://github.com/ded/bowser/blob/master/bowser.js + if ((/(msie|trident)/i).test(agentString)) { + var match = agentString.match(/(msie |rv:)(\d+(.\d+)?)/i); + if (match) { return match[2]; } + } + return false; + }, + + // http://stackoverflow.com/a/6969486 + escapeRegExChars: function(str) { + return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&'); + }, + + isNumber: function(obj) { return typeof obj === 'number'; }, + + toStr: function toStr(s) { + return s === undefined || s === null ? '' : s + ''; + }, + + cloneDeep: function cloneDeep(obj) { + var clone = this.mixin({}, obj); + var self = this; + this.each(clone, function(value, key) { + if (value) { + if (self.isArray(value)) { + clone[key] = [].concat(value); + } else if (self.isObject(value)) { + clone[key] = self.cloneDeep(value); + } + } + }); + return clone; + }, + + error: function(msg) { + throw new Error(msg); + }, + + every: function(obj, test) { + var result = true; + if (!obj) { + return result; + } + this.each(obj, function(val, key) { + if (result) { + result = test.call(null, val, key, obj) && result; + } + }); + return !!result; + }, + + any: function(obj, test) { + var found = false; + if (!obj) { + return found; + } + this.each(obj, function(val, key) { + if (test.call(null, val, key, obj)) { + found = true; + return false; + } + }); + return found; + }, + + getUniqueId: (function() { + var counter = 0; + return function() { return counter++; }; + })(), + + templatify: function templatify(obj) { + if (this.isFunction(obj)) { + return obj; + } + var $template = DOM.element(obj); + if ($template.prop('tagName') === 'SCRIPT') { + return function template() { return $template.text(); }; + } + return function template() { return String(obj); }; + }, + + defer: function(fn) { setTimeout(fn, 0); }, + + noop: function() {}, + + formatPrefix: function(prefix, noPrefix) { + return noPrefix ? '' : prefix + '-'; + }, + + className: function(prefix, clazz, skipDot) { + return (skipDot ? '' : '.') + prefix + clazz; + }, + + escapeHighlightedString: function(str, highlightPreTag, highlightPostTag) { + highlightPreTag = highlightPreTag || ''; + var pre = document.createElement('div'); + pre.appendChild(document.createTextNode(highlightPreTag)); + + highlightPostTag = highlightPostTag || ''; + var post = document.createElement('div'); + post.appendChild(document.createTextNode(highlightPostTag)); + + var div = document.createElement('div'); + div.appendChild(document.createTextNode(str)); + return div.innerHTML + .replace(RegExp(escapeRegExp(pre.innerHTML), 'g'), highlightPreTag) + .replace(RegExp(escapeRegExp(post.innerHTML), 'g'), highlightPostTag); + } + }; + + +/***/ }, +/* 5 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + var attrsKey = 'aaAttrs'; + + var _ = __webpack_require__(4); + var DOM = __webpack_require__(2); + var EventBus = __webpack_require__(6); + var Input = __webpack_require__(7); + var Dropdown = __webpack_require__(16); + var html = __webpack_require__(18); + var css = __webpack_require__(19); + + // constructor + // ----------- + + // THOUGHT: what if datasets could dynamically be added/removed? + function Typeahead(o) { + var $menu; + var $hint; + + o = o || {}; + + if (!o.input) { + _.error('missing input'); + } + + this.isActivated = false; + this.debug = !!o.debug; + this.autoselect = !!o.autoselect; + this.autoselectOnBlur = !!o.autoselectOnBlur; + this.openOnFocus = !!o.openOnFocus; + this.minLength = _.isNumber(o.minLength) ? o.minLength : 1; + this.autoWidth = (o.autoWidth === undefined) ? true : !!o.autoWidth; + this.clearOnSelected = !!o.clearOnSelected; + this.tabAutocomplete = (o.tabAutocomplete === undefined) ? true : !!o.tabAutocomplete; + + o.hint = !!o.hint; + + if (o.hint && o.appendTo) { + throw new Error('[autocomplete.js] hint and appendTo options can\'t be used at the same time'); + } + + this.css = o.css = _.mixin({}, css, o.appendTo ? css.appendTo : {}); + this.cssClasses = o.cssClasses = _.mixin({}, css.defaultClasses, o.cssClasses || {}); + this.cssClasses.prefix = + o.cssClasses.formattedPrefix = _.formatPrefix(this.cssClasses.prefix, this.cssClasses.noPrefix); + this.listboxId = o.listboxId = [this.cssClasses.root, 'listbox', _.getUniqueId()].join('-'); + + var domElts = buildDom(o); + + this.$node = domElts.wrapper; + var $input = this.$input = domElts.input; + $menu = domElts.menu; + $hint = domElts.hint; + + if (o.dropdownMenuContainer) { + DOM.element(o.dropdownMenuContainer) + .css('position', 'relative') // ensure the container has a relative position + .append($menu.css('top', '0')); // override the top: 100% + } + + // #705: if there's scrollable overflow, ie doesn't support + // blur cancellations when the scrollbar is clicked + // + // #351: preventDefault won't cancel blurs in ie <= 8 + $input.on('blur.aa', function($e) { + var active = document.activeElement; + if (_.isMsie() && ($menu[0] === active || $menu[0].contains(active))) { + $e.preventDefault(); + // stop immediate in order to prevent Input#_onBlur from + // getting exectued + $e.stopImmediatePropagation(); + _.defer(function() { $input.focus(); }); + } + }); + + // #351: prevents input blur due to clicks within dropdown menu + $menu.on('mousedown.aa', function($e) { $e.preventDefault(); }); + + this.eventBus = o.eventBus || new EventBus({el: $input}); + + this.dropdown = new Typeahead.Dropdown({ + appendTo: o.appendTo, + wrapper: this.$node, + menu: $menu, + datasets: o.datasets, + templates: o.templates, + cssClasses: o.cssClasses, + minLength: this.minLength + }) + .onSync('suggestionClicked', this._onSuggestionClicked, this) + .onSync('cursorMoved', this._onCursorMoved, this) + .onSync('cursorRemoved', this._onCursorRemoved, this) + .onSync('opened', this._onOpened, this) + .onSync('closed', this._onClosed, this) + .onSync('shown', this._onShown, this) + .onSync('empty', this._onEmpty, this) + .onSync('redrawn', this._onRedrawn, this) + .onAsync('datasetRendered', this._onDatasetRendered, this); + + this.input = new Typeahead.Input({input: $input, hint: $hint}) + .onSync('focused', this._onFocused, this) + .onSync('blurred', this._onBlurred, this) + .onSync('enterKeyed', this._onEnterKeyed, this) + .onSync('tabKeyed', this._onTabKeyed, this) + .onSync('escKeyed', this._onEscKeyed, this) + .onSync('upKeyed', this._onUpKeyed, this) + .onSync('downKeyed', this._onDownKeyed, this) + .onSync('leftKeyed', this._onLeftKeyed, this) + .onSync('rightKeyed', this._onRightKeyed, this) + .onSync('queryChanged', this._onQueryChanged, this) + .onSync('whitespaceChanged', this._onWhitespaceChanged, this); + + this._bindKeyboardShortcuts(o); + + this._setLanguageDirection(); + } + + // instance methods + // ---------------- + + _.mixin(Typeahead.prototype, { + // ### private + + _bindKeyboardShortcuts: function(options) { + if (!options.keyboardShortcuts) { + return; + } + var $input = this.$input; + var keyboardShortcuts = []; + _.each(options.keyboardShortcuts, function(key) { + if (typeof key === 'string') { + key = key.toUpperCase().charCodeAt(0); + } + keyboardShortcuts.push(key); + }); + DOM.element(document).keydown(function(event) { + var elt = (event.target || event.srcElement); + var tagName = elt.tagName; + if (elt.isContentEditable || tagName === 'INPUT' || tagName === 'SELECT' || tagName === 'TEXTAREA') { + // already in an input + return; + } + + var which = event.which || event.keyCode; + if (keyboardShortcuts.indexOf(which) === -1) { + // not the right shortcut + return; + } + + $input.focus(); + event.stopPropagation(); + event.preventDefault(); + }); + }, + + _onSuggestionClicked: function onSuggestionClicked(type, $el) { + var datum; + var context = {selectionMethod: 'click'}; + + if (datum = this.dropdown.getDatumForSuggestion($el)) { + this._select(datum, context); + } + }, + + _onCursorMoved: function onCursorMoved(event, updateInput) { + var datum = this.dropdown.getDatumForCursor(); + var currentCursorId = this.dropdown.getCurrentCursor().attr('id'); + this.input.setActiveDescendant(currentCursorId); + + if (datum) { + if (updateInput) { + this.input.setInputValue(datum.value, true); + } + + this.eventBus.trigger('cursorchanged', datum.raw, datum.datasetName); + } + }, + + _onCursorRemoved: function onCursorRemoved() { + this.input.resetInputValue(); + this._updateHint(); + this.eventBus.trigger('cursorremoved'); + }, + + _onDatasetRendered: function onDatasetRendered() { + this._updateHint(); + + this.eventBus.trigger('updated'); + }, + + _onOpened: function onOpened() { + this._updateHint(); + this.input.expand(); + + this.eventBus.trigger('opened'); + }, + + _onEmpty: function onEmpty() { + this.eventBus.trigger('empty'); + }, + + _onRedrawn: function onRedrawn() { + this.$node.css('top', 0 + 'px'); + this.$node.css('left', 0 + 'px'); + + var inputRect = this.$input[0].getBoundingClientRect(); + + if (this.autoWidth) { + this.$node.css('width', inputRect.width + 'px'); + } + + var wrapperRect = this.$node[0].getBoundingClientRect(); + + var top = inputRect.bottom - wrapperRect.top; + this.$node.css('top', top + 'px'); + var left = inputRect.left - wrapperRect.left; + this.$node.css('left', left + 'px'); + + this.eventBus.trigger('redrawn'); + }, + + _onShown: function onShown() { + this.eventBus.trigger('shown'); + if (this.autoselect) { + this.dropdown.cursorTopSuggestion(); + } + }, + + _onClosed: function onClosed() { + this.input.clearHint(); + this.input.removeActiveDescendant(); + this.input.collapse(); + + this.eventBus.trigger('closed'); + }, + + _onFocused: function onFocused() { + this.isActivated = true; + + if (this.openOnFocus) { + var query = this.input.getQuery(); + if (query.length >= this.minLength) { + this.dropdown.update(query); + } else { + this.dropdown.empty(); + } + + this.dropdown.open(); + } + }, + + _onBlurred: function onBlurred() { + var cursorDatum; + var topSuggestionDatum; + + cursorDatum = this.dropdown.getDatumForCursor(); + topSuggestionDatum = this.dropdown.getDatumForTopSuggestion(); + var context = {selectionMethod: 'blur'}; + + if (!this.debug) { + if (this.autoselectOnBlur && cursorDatum) { + this._select(cursorDatum, context); + } else if (this.autoselectOnBlur && topSuggestionDatum) { + this._select(topSuggestionDatum, context); + } else { + this.isActivated = false; + this.dropdown.empty(); + this.dropdown.close(); + } + } + }, + + _onEnterKeyed: function onEnterKeyed(type, $e) { + var cursorDatum; + var topSuggestionDatum; + + cursorDatum = this.dropdown.getDatumForCursor(); + topSuggestionDatum = this.dropdown.getDatumForTopSuggestion(); + var context = {selectionMethod: 'enterKey'}; + + if (cursorDatum) { + this._select(cursorDatum, context); + $e.preventDefault(); + } else if (this.autoselect && topSuggestionDatum) { + this._select(topSuggestionDatum, context); + $e.preventDefault(); + } + }, + + _onTabKeyed: function onTabKeyed(type, $e) { + if (!this.tabAutocomplete) { + // Closing the dropdown enables further tabbing + this.dropdown.close(); + return; + } + + var datum; + var context = {selectionMethod: 'tabKey'}; + + if (datum = this.dropdown.getDatumForCursor()) { + this._select(datum, context); + $e.preventDefault(); + } else { + this._autocomplete(true); + } + }, + + _onEscKeyed: function onEscKeyed() { + this.dropdown.close(); + this.input.resetInputValue(); + }, + + _onUpKeyed: function onUpKeyed() { + var query = this.input.getQuery(); + + if (this.dropdown.isEmpty && query.length >= this.minLength) { + this.dropdown.update(query); + } else { + this.dropdown.moveCursorUp(); + } + + this.dropdown.open(); + }, + + _onDownKeyed: function onDownKeyed() { + var query = this.input.getQuery(); + + if (this.dropdown.isEmpty && query.length >= this.minLength) { + this.dropdown.update(query); + } else { + this.dropdown.moveCursorDown(); + } + + this.dropdown.open(); + }, + + _onLeftKeyed: function onLeftKeyed() { + if (this.dir === 'rtl') { + this._autocomplete(); + } + }, + + _onRightKeyed: function onRightKeyed() { + if (this.dir === 'ltr') { + this._autocomplete(); + } + }, + + _onQueryChanged: function onQueryChanged(e, query) { + this.input.clearHintIfInvalid(); + + if (query.length >= this.minLength) { + this.dropdown.update(query); + } else { + this.dropdown.empty(); + } + + this.dropdown.open(); + this._setLanguageDirection(); + }, + + _onWhitespaceChanged: function onWhitespaceChanged() { + this._updateHint(); + this.dropdown.open(); + }, + + _setLanguageDirection: function setLanguageDirection() { + var dir = this.input.getLanguageDirection(); + + if (this.dir !== dir) { + this.dir = dir; + this.$node.css('direction', dir); + this.dropdown.setLanguageDirection(dir); + } + }, + + _updateHint: function updateHint() { + var datum; + var val; + var query; + var escapedQuery; + var frontMatchRegEx; + var match; + + datum = this.dropdown.getDatumForTopSuggestion(); + + if (datum && this.dropdown.isVisible() && !this.input.hasOverflow()) { + val = this.input.getInputValue(); + query = Input.normalizeQuery(val); + escapedQuery = _.escapeRegExChars(query); + + // match input value, then capture trailing text + frontMatchRegEx = new RegExp('^(?:' + escapedQuery + ')(.+$)', 'i'); + match = frontMatchRegEx.exec(datum.value); + + // clear hint if there's no trailing text + if (match) { + this.input.setHint(val + match[1]); + } else { + this.input.clearHint(); + } + } else { + this.input.clearHint(); + } + }, + + _autocomplete: function autocomplete(laxCursor) { + var hint; + var query; + var isCursorAtEnd; + var datum; + + hint = this.input.getHint(); + query = this.input.getQuery(); + isCursorAtEnd = laxCursor || this.input.isCursorAtEnd(); + + if (hint && query !== hint && isCursorAtEnd) { + datum = this.dropdown.getDatumForTopSuggestion(); + if (datum) { + this.input.setInputValue(datum.value); + } + + this.eventBus.trigger('autocompleted', datum.raw, datum.datasetName); + } + }, + + _select: function select(datum, context) { + if (typeof datum.value !== 'undefined') { + this.input.setQuery(datum.value); + } + if (this.clearOnSelected) { + this.setVal(''); + } else { + this.input.setInputValue(datum.value, true); + } + + this._setLanguageDirection(); + + var event = this.eventBus.trigger('selected', datum.raw, datum.datasetName, context); + if (event.isDefaultPrevented() === false) { + this.dropdown.close(); + + // #118: allow click event to bubble up to the body before removing + // the suggestions otherwise we break event delegation + _.defer(_.bind(this.dropdown.empty, this.dropdown)); + } + }, + + // ### public + + open: function open() { + // if the menu is not activated yet, we need to update + // the underlying dropdown menu to trigger the search + // otherwise we're not gonna see anything + if (!this.isActivated) { + var query = this.input.getInputValue(); + if (query.length >= this.minLength) { + this.dropdown.update(query); + } else { + this.dropdown.empty(); + } + } + this.dropdown.open(); + }, + + close: function close() { + this.dropdown.close(); + }, + + setVal: function setVal(val) { + // expect val to be a string, so be safe, and coerce + val = _.toStr(val); + + if (this.isActivated) { + this.input.setInputValue(val); + } else { + this.input.setQuery(val); + this.input.setInputValue(val, true); + } + + this._setLanguageDirection(); + }, + + getVal: function getVal() { + return this.input.getQuery(); + }, + + destroy: function destroy() { + this.input.destroy(); + this.dropdown.destroy(); + + destroyDomStructure(this.$node, this.cssClasses); + + this.$node = null; + }, + + getWrapper: function getWrapper() { + return this.dropdown.$container[0]; + } + }); + + function buildDom(options) { + var $input; + var $wrapper; + var $dropdown; + var $hint; + + $input = DOM.element(options.input); + $wrapper = DOM + .element(html.wrapper.replace('%ROOT%', options.cssClasses.root)) + .css(options.css.wrapper); + + // override the display property with the table-cell value + // if the parent element is a table and the original input was a block + // -> https://github.com/algolia/autocomplete.js/issues/16 + if (!options.appendTo && $input.css('display') === 'block' && $input.parent().css('display') === 'table') { + $wrapper.css('display', 'table-cell'); + } + var dropdownHtml = html.dropdown. + replace('%PREFIX%', options.cssClasses.prefix). + replace('%DROPDOWN_MENU%', options.cssClasses.dropdownMenu); + $dropdown = DOM.element(dropdownHtml) + .css(options.css.dropdown) + .attr({ + role: 'listbox', + id: options.listboxId + }); + if (options.templates && options.templates.dropdownMenu) { + $dropdown.html(_.templatify(options.templates.dropdownMenu)()); + } + $hint = $input.clone().css(options.css.hint).css(getBackgroundStyles($input)); + + $hint + .val('') + .addClass(_.className(options.cssClasses.prefix, options.cssClasses.hint, true)) + .removeAttr('id name placeholder required') + .prop('readonly', true) + .attr({ + 'aria-hidden': 'true', + autocomplete: 'off', + spellcheck: 'false', + tabindex: -1 + }); + if ($hint.removeData) { + $hint.removeData(); + } + + // store the original values of the attrs that get modified + // so modifications can be reverted on destroy + $input.data(attrsKey, { + 'aria-autocomplete': $input.attr('aria-autocomplete'), + 'aria-expanded': $input.attr('aria-expanded'), + 'aria-owns': $input.attr('aria-owns'), + autocomplete: $input.attr('autocomplete'), + dir: $input.attr('dir'), + role: $input.attr('role'), + spellcheck: $input.attr('spellcheck'), + style: $input.attr('style'), + type: $input.attr('type') + }); + + $input + .addClass(_.className(options.cssClasses.prefix, options.cssClasses.input, true)) + .attr({ + autocomplete: 'off', + spellcheck: false, + + // Accessibility features + // Give the field a presentation of a "select". + // Combobox is the combined presentation of a single line textfield + // with a listbox popup. + // https://www.w3.org/WAI/PF/aria/roles#combobox + role: 'combobox', + // Let the screen reader know the field has an autocomplete + // feature to it. + 'aria-autocomplete': (options.datasets && + options.datasets[0] && options.datasets[0].displayKey ? 'both' : 'list'), + // Indicates whether the dropdown it controls is currently expanded or collapsed + 'aria-expanded': 'false', + 'aria-label': options.ariaLabel, + // Explicitly point to the listbox, + // which is a list of suggestions (aka options) + 'aria-owns': options.listboxId + }) + .css(options.hint ? options.css.input : options.css.inputWithNoHint); + + // ie7 does not like it when dir is set to auto + try { + if (!$input.attr('dir')) { + $input.attr('dir', 'auto'); + } + } catch (e) { + // ignore + } + + $wrapper = options.appendTo + ? $wrapper.appendTo(DOM.element(options.appendTo).eq(0)).eq(0) + : $input.wrap($wrapper).parent(); + + $wrapper + .prepend(options.hint ? $hint : null) + .append($dropdown); + + return { + wrapper: $wrapper, + input: $input, + hint: $hint, + menu: $dropdown + }; + } + + function getBackgroundStyles($el) { + return { + backgroundAttachment: $el.css('background-attachment'), + backgroundClip: $el.css('background-clip'), + backgroundColor: $el.css('background-color'), + backgroundImage: $el.css('background-image'), + backgroundOrigin: $el.css('background-origin'), + backgroundPosition: $el.css('background-position'), + backgroundRepeat: $el.css('background-repeat'), + backgroundSize: $el.css('background-size') + }; + } + + function destroyDomStructure($node, cssClasses) { + var $input = $node.find(_.className(cssClasses.prefix, cssClasses.input)); + + // need to remove attrs that weren't previously defined and + // revert attrs that originally had a value + _.each($input.data(attrsKey), function(val, key) { + if (val === undefined) { + $input.removeAttr(key); + } else { + $input.attr(key, val); + } + }); + + $input + .detach() + .removeClass(_.className(cssClasses.prefix, cssClasses.input, true)) + .insertAfter($node); + if ($input.removeData) { + $input.removeData(attrsKey); + } + + $node.remove(); + } + + Typeahead.Dropdown = Dropdown; + Typeahead.Input = Input; + Typeahead.sources = __webpack_require__(20); + + module.exports = Typeahead; + + +/***/ }, +/* 6 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + var namespace = 'autocomplete:'; + + var _ = __webpack_require__(4); + var DOM = __webpack_require__(2); + + // constructor + // ----------- + + function EventBus(o) { + if (!o || !o.el) { + _.error('EventBus initialized without el'); + } + + this.$el = DOM.element(o.el); + } + + // instance methods + // ---------------- + + _.mixin(EventBus.prototype, { + + // ### public + + trigger: function(type, suggestion, dataset, context) { + var event = _.Event(namespace + type); + this.$el.trigger(event, [suggestion, dataset, context]); + return event; + } + }); + + module.exports = EventBus; + + +/***/ }, +/* 7 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + var specialKeyCodeMap; + + specialKeyCodeMap = { + 9: 'tab', + 27: 'esc', + 37: 'left', + 39: 'right', + 13: 'enter', + 38: 'up', + 40: 'down' + }; + + var _ = __webpack_require__(4); + var DOM = __webpack_require__(2); + var EventEmitter = __webpack_require__(8); + + // constructor + // ----------- + + function Input(o) { + var that = this; + var onBlur; + var onFocus; + var onKeydown; + var onInput; + + o = o || {}; + + if (!o.input) { + _.error('input is missing'); + } + + // bound functions + onBlur = _.bind(this._onBlur, this); + onFocus = _.bind(this._onFocus, this); + onKeydown = _.bind(this._onKeydown, this); + onInput = _.bind(this._onInput, this); + + this.$hint = DOM.element(o.hint); + this.$input = DOM.element(o.input) + .on('blur.aa', onBlur) + .on('focus.aa', onFocus) + .on('keydown.aa', onKeydown); + + // if no hint, noop all the hint related functions + if (this.$hint.length === 0) { + this.setHint = this.getHint = this.clearHint = this.clearHintIfInvalid = _.noop; + } + + // ie7 and ie8 don't support the input event + // ie9 doesn't fire the input event when characters are removed + // not sure if ie10 is compatible + if (!_.isMsie()) { + this.$input.on('input.aa', onInput); + } else { + this.$input.on('keydown.aa keypress.aa cut.aa paste.aa', function($e) { + // if a special key triggered this, ignore it + if (specialKeyCodeMap[$e.which || $e.keyCode]) { + return; + } + + // give the browser a chance to update the value of the input + // before checking to see if the query changed + _.defer(_.bind(that._onInput, that, $e)); + }); + } + + // the query defaults to whatever the value of the input is + // on initialization, it'll most likely be an empty string + this.query = this.$input.val(); + + // helps with calculating the width of the input's value + this.$overflowHelper = buildOverflowHelper(this.$input); + } + + // static methods + // -------------- + + Input.normalizeQuery = function(str) { + // strips leading whitespace and condenses all whitespace + return (str || '').replace(/^\s*/g, '').replace(/\s{2,}/g, ' '); + }; + + // instance methods + // ---------------- + + _.mixin(Input.prototype, EventEmitter, { + + // ### private + + _onBlur: function onBlur() { + this.resetInputValue(); + this.$input.removeAttr('aria-activedescendant'); + this.trigger('blurred'); + }, + + _onFocus: function onFocus() { + this.trigger('focused'); + }, + + _onKeydown: function onKeydown($e) { + // which is normalized and consistent (but not for ie) + var keyName = specialKeyCodeMap[$e.which || $e.keyCode]; + + this._managePreventDefault(keyName, $e); + if (keyName && this._shouldTrigger(keyName, $e)) { + this.trigger(keyName + 'Keyed', $e); + } + }, + + _onInput: function onInput() { + this._checkInputValue(); + }, + + _managePreventDefault: function managePreventDefault(keyName, $e) { + var preventDefault; + var hintValue; + var inputValue; + + switch (keyName) { + case 'tab': + hintValue = this.getHint(); + inputValue = this.getInputValue(); + + preventDefault = hintValue && + hintValue !== inputValue && + !withModifier($e); + break; + + case 'up': + case 'down': + preventDefault = !withModifier($e); + break; + + default: + preventDefault = false; + } + + if (preventDefault) { + $e.preventDefault(); + } + }, + + _shouldTrigger: function shouldTrigger(keyName, $e) { + var trigger; + + switch (keyName) { + case 'tab': + trigger = !withModifier($e); + break; + + default: + trigger = true; + } + + return trigger; + }, + + _checkInputValue: function checkInputValue() { + var inputValue; + var areEquivalent; + var hasDifferentWhitespace; + + inputValue = this.getInputValue(); + areEquivalent = areQueriesEquivalent(inputValue, this.query); + hasDifferentWhitespace = areEquivalent && this.query ? + this.query.length !== inputValue.length : false; + + this.query = inputValue; + + if (!areEquivalent) { + this.trigger('queryChanged', this.query); + } else if (hasDifferentWhitespace) { + this.trigger('whitespaceChanged', this.query); + } + }, + + // ### public + + focus: function focus() { + this.$input.focus(); + }, + + blur: function blur() { + this.$input.blur(); + }, + + getQuery: function getQuery() { + return this.query; + }, + + setQuery: function setQuery(query) { + this.query = query; + }, + + getInputValue: function getInputValue() { + return this.$input.val(); + }, + + setInputValue: function setInputValue(value, silent) { + if (typeof value === 'undefined') { + value = this.query; + } + this.$input.val(value); + + // silent prevents any additional events from being triggered + if (silent) { + this.clearHint(); + } else { + this._checkInputValue(); + } + }, + + expand: function expand() { + this.$input.attr('aria-expanded', 'true'); + }, + + collapse: function collapse() { + this.$input.attr('aria-expanded', 'false'); + }, + + setActiveDescendant: function setActiveDescendant(activedescendantId) { + this.$input.attr('aria-activedescendant', activedescendantId); + }, + + removeActiveDescendant: function removeActiveDescendant() { + this.$input.removeAttr('aria-activedescendant'); + }, + + resetInputValue: function resetInputValue() { + this.setInputValue(this.query, true); + }, + + getHint: function getHint() { + return this.$hint.val(); + }, + + setHint: function setHint(value) { + this.$hint.val(value); + }, + + clearHint: function clearHint() { + this.setHint(''); + }, + + clearHintIfInvalid: function clearHintIfInvalid() { + var val; + var hint; + var valIsPrefixOfHint; + var isValid; + + val = this.getInputValue(); + hint = this.getHint(); + valIsPrefixOfHint = val !== hint && hint.indexOf(val) === 0; + isValid = val !== '' && valIsPrefixOfHint && !this.hasOverflow(); + + if (!isValid) { + this.clearHint(); + } + }, + + getLanguageDirection: function getLanguageDirection() { + return (this.$input.css('direction') || 'ltr').toLowerCase(); + }, + + hasOverflow: function hasOverflow() { + // 2 is arbitrary, just picking a small number to handle edge cases + var constraint = this.$input.width() - 2; + + this.$overflowHelper.text(this.getInputValue()); + + return this.$overflowHelper.width() >= constraint; + }, + + isCursorAtEnd: function() { + var valueLength; + var selectionStart; + var range; + + valueLength = this.$input.val().length; + selectionStart = this.$input[0].selectionStart; + + if (_.isNumber(selectionStart)) { + return selectionStart === valueLength; + } else if (document.selection) { + // NOTE: this won't work unless the input has focus, the good news + // is this code should only get called when the input has focus + range = document.selection.createRange(); + range.moveStart('character', -valueLength); + + return valueLength === range.text.length; + } + + return true; + }, + + destroy: function destroy() { + this.$hint.off('.aa'); + this.$input.off('.aa'); + + this.$hint = this.$input = this.$overflowHelper = null; + } + }); + + // helper functions + // ---------------- + + function buildOverflowHelper($input) { + return DOM.element('') + .css({ + // position helper off-screen + position: 'absolute', + visibility: 'hidden', + // avoid line breaks and whitespace collapsing + whiteSpace: 'pre', + // use same font css as input to calculate accurate width + fontFamily: $input.css('font-family'), + fontSize: $input.css('font-size'), + fontStyle: $input.css('font-style'), + fontVariant: $input.css('font-variant'), + fontWeight: $input.css('font-weight'), + wordSpacing: $input.css('word-spacing'), + letterSpacing: $input.css('letter-spacing'), + textIndent: $input.css('text-indent'), + textRendering: $input.css('text-rendering'), + textTransform: $input.css('text-transform') + }) + .insertAfter($input); + } + + function areQueriesEquivalent(a, b) { + return Input.normalizeQuery(a) === Input.normalizeQuery(b); + } + + function withModifier($e) { + return $e.altKey || $e.ctrlKey || $e.metaKey || $e.shiftKey; + } + + module.exports = Input; + + +/***/ }, +/* 8 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + var immediate = __webpack_require__(9); + var splitter = /\s+/; + + module.exports = { + onSync: onSync, + onAsync: onAsync, + off: off, + trigger: trigger + }; + + function on(method, types, cb, context) { + var type; + + if (!cb) { + return this; + } + + types = types.split(splitter); + cb = context ? bindContext(cb, context) : cb; + + this._callbacks = this._callbacks || {}; + + while (type = types.shift()) { + this._callbacks[type] = this._callbacks[type] || {sync: [], async: []}; + this._callbacks[type][method].push(cb); + } + + return this; + } + + function onAsync(types, cb, context) { + return on.call(this, 'async', types, cb, context); + } + + function onSync(types, cb, context) { + return on.call(this, 'sync', types, cb, context); + } + + function off(types) { + var type; + + if (!this._callbacks) { + return this; + } + + types = types.split(splitter); + + while (type = types.shift()) { + delete this._callbacks[type]; + } + + return this; + } + + function trigger(types) { + var type; + var callbacks; + var args; + var syncFlush; + var asyncFlush; + + if (!this._callbacks) { + return this; + } + + types = types.split(splitter); + args = [].slice.call(arguments, 1); + + while ((type = types.shift()) && (callbacks = this._callbacks[type])) { // eslint-disable-line + syncFlush = getFlush(callbacks.sync, this, [type].concat(args)); + asyncFlush = getFlush(callbacks.async, this, [type].concat(args)); + + if (syncFlush()) { + immediate(asyncFlush); + } + } + + return this; + } + + function getFlush(callbacks, context, args) { + return flush; + + function flush() { + var cancelled; + + for (var i = 0, len = callbacks.length; !cancelled && i < len; i += 1) { + // only cancel if the callback explicitly returns false + cancelled = callbacks[i].apply(context, args) === false; + } + + return !cancelled; + } + } + + function bindContext(fn, context) { + return fn.bind ? + fn.bind(context) : + function() { fn.apply(context, [].slice.call(arguments, 0)); }; + } + + +/***/ }, +/* 9 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + var types = [ + __webpack_require__(10), + __webpack_require__(12), + __webpack_require__(13), + __webpack_require__(14), + __webpack_require__(15) + ]; + var draining; + var currentQueue; + var queueIndex = -1; + var queue = []; + var scheduled = false; + function cleanUpNextTick() { + if (!draining || !currentQueue) { + return; + } + draining = false; + if (currentQueue.length) { + queue = currentQueue.concat(queue); + } else { + queueIndex = -1; + } + if (queue.length) { + nextTick(); + } + } + + //named nextTick for less confusing stack traces + function nextTick() { + if (draining) { + return; + } + scheduled = false; + draining = true; + var len = queue.length; + var timeout = setTimeout(cleanUpNextTick); + while (len) { + currentQueue = queue; + queue = []; + while (currentQueue && ++queueIndex < len) { + currentQueue[queueIndex].run(); + } + queueIndex = -1; + len = queue.length; + } + currentQueue = null; + queueIndex = -1; + draining = false; + clearTimeout(timeout); + } + var scheduleDrain; + var i = -1; + var len = types.length; + while (++i < len) { + if (types[i] && types[i].test && types[i].test()) { + scheduleDrain = types[i].install(nextTick); + break; + } + } + // v8 likes predictible objects + function Item(fun, array) { + this.fun = fun; + this.array = array; + } + Item.prototype.run = function () { + var fun = this.fun; + var array = this.array; + switch (array.length) { + case 0: + return fun(); + case 1: + return fun(array[0]); + case 2: + return fun(array[0], array[1]); + case 3: + return fun(array[0], array[1], array[2]); + default: + return fun.apply(null, array); + } + + }; + module.exports = immediate; + function immediate(task) { + var args = new Array(arguments.length - 1); + if (arguments.length > 1) { + for (var i = 1; i < arguments.length; i++) { + args[i - 1] = arguments[i]; + } + } + queue.push(new Item(task, args)); + if (!scheduled && !draining) { + scheduled = true; + scheduleDrain(); + } + } + + +/***/ }, +/* 10 */ +/***/ function(module, exports, __webpack_require__) { + + /* WEBPACK VAR INJECTION */(function(process) {'use strict'; + exports.test = function () { + // Don't get fooled by e.g. browserify environments. + return (typeof process !== 'undefined') && !process.browser; + }; + + exports.install = function (func) { + return function () { + process.nextTick(func); + }; + }; + + /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(11))) + +/***/ }, +/* 11 */ +/***/ function(module, exports) { + + // shim for using process in browser + var process = module.exports = {}; + + // cached from whatever global is present so that test runners that stub it + // don't break things. But we need to wrap it in a try catch in case it is + // wrapped in strict mode code which doesn't define any globals. It's inside a + // function because try/catches deoptimize in certain engines. + + var cachedSetTimeout; + var cachedClearTimeout; + + function defaultSetTimout() { + throw new Error('setTimeout has not been defined'); + } + function defaultClearTimeout () { + throw new Error('clearTimeout has not been defined'); + } + (function () { + try { + if (typeof setTimeout === 'function') { + cachedSetTimeout = setTimeout; + } else { + cachedSetTimeout = defaultSetTimout; + } + } catch (e) { + cachedSetTimeout = defaultSetTimout; + } + try { + if (typeof clearTimeout === 'function') { + cachedClearTimeout = clearTimeout; + } else { + cachedClearTimeout = defaultClearTimeout; + } + } catch (e) { + cachedClearTimeout = defaultClearTimeout; + } + } ()) + function runTimeout(fun) { + if (cachedSetTimeout === setTimeout) { + //normal enviroments in sane situations + return setTimeout(fun, 0); + } + // if setTimeout wasn't available but was latter defined + if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) { + cachedSetTimeout = setTimeout; + return setTimeout(fun, 0); + } + try { + // when when somebody has screwed with setTimeout but no I.E. maddness + return cachedSetTimeout(fun, 0); + } catch(e){ + try { + // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally + return cachedSetTimeout.call(null, fun, 0); + } catch(e){ + // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error + return cachedSetTimeout.call(this, fun, 0); + } + } + + + } + function runClearTimeout(marker) { + if (cachedClearTimeout === clearTimeout) { + //normal enviroments in sane situations + return clearTimeout(marker); + } + // if clearTimeout wasn't available but was latter defined + if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) { + cachedClearTimeout = clearTimeout; + return clearTimeout(marker); + } + try { + // when when somebody has screwed with setTimeout but no I.E. maddness + return cachedClearTimeout(marker); + } catch (e){ + try { + // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally + return cachedClearTimeout.call(null, marker); + } catch (e){ + // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error. + // Some versions of I.E. have different rules for clearTimeout vs setTimeout + return cachedClearTimeout.call(this, marker); + } + } + + + + } + var queue = []; + var draining = false; + var currentQueue; + var queueIndex = -1; + + function cleanUpNextTick() { + if (!draining || !currentQueue) { + return; + } + draining = false; + if (currentQueue.length) { + queue = currentQueue.concat(queue); + } else { + queueIndex = -1; + } + if (queue.length) { + drainQueue(); + } + } + + function drainQueue() { + if (draining) { + return; + } + var timeout = runTimeout(cleanUpNextTick); + draining = true; + + var len = queue.length; + while(len) { + currentQueue = queue; + queue = []; + while (++queueIndex < len) { + if (currentQueue) { + currentQueue[queueIndex].run(); + } + } + queueIndex = -1; + len = queue.length; + } + currentQueue = null; + draining = false; + runClearTimeout(timeout); + } + + process.nextTick = function (fun) { + var args = new Array(arguments.length - 1); + if (arguments.length > 1) { + for (var i = 1; i < arguments.length; i++) { + args[i - 1] = arguments[i]; + } + } + queue.push(new Item(fun, args)); + if (queue.length === 1 && !draining) { + runTimeout(drainQueue); + } + }; + + // v8 likes predictible objects + function Item(fun, array) { + this.fun = fun; + this.array = array; + } + Item.prototype.run = function () { + this.fun.apply(null, this.array); + }; + process.title = 'browser'; + process.browser = true; + process.env = {}; + process.argv = []; + process.version = ''; // empty string to avoid regexp issues + process.versions = {}; + + function noop() {} + + process.on = noop; + process.addListener = noop; + process.once = noop; + process.off = noop; + process.removeListener = noop; + process.removeAllListeners = noop; + process.emit = noop; + + process.binding = function (name) { + throw new Error('process.binding is not supported'); + }; + + process.cwd = function () { return '/' }; + process.chdir = function (dir) { + throw new Error('process.chdir is not supported'); + }; + process.umask = function() { return 0; }; + + +/***/ }, +/* 12 */ +/***/ function(module, exports) { + + /* WEBPACK VAR INJECTION */(function(global) {'use strict'; + //based off rsvp https://github.com/tildeio/rsvp.js + //license https://github.com/tildeio/rsvp.js/blob/master/LICENSE + //https://github.com/tildeio/rsvp.js/blob/master/lib/rsvp/asap.js + + var Mutation = global.MutationObserver || global.WebKitMutationObserver; + + exports.test = function () { + return Mutation; + }; + + exports.install = function (handle) { + var called = 0; + var observer = new Mutation(handle); + var element = global.document.createTextNode(''); + observer.observe(element, { + characterData: true + }); + return function () { + element.data = (called = ++called % 2); + }; + }; + /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }()))) + +/***/ }, +/* 13 */ +/***/ function(module, exports) { + + /* WEBPACK VAR INJECTION */(function(global) {'use strict'; + + exports.test = function () { + if (global.setImmediate) { + // we can only get here in IE10 + // which doesn't handel postMessage well + return false; + } + return typeof global.MessageChannel !== 'undefined'; + }; + + exports.install = function (func) { + var channel = new global.MessageChannel(); + channel.port1.onmessage = func; + return function () { + channel.port2.postMessage(0); + }; + }; + /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }()))) + +/***/ }, +/* 14 */ +/***/ function(module, exports) { + + /* WEBPACK VAR INJECTION */(function(global) {'use strict'; + + exports.test = function () { + return 'document' in global && 'onreadystatechange' in global.document.createElement('script'); + }; + + exports.install = function (handle) { + return function () { + + // Create a