Echo/node_modules/autocomplete.js/dist/autocomplete.jquery.js

2919 lines
75 KiB
JavaScript
Raw Normal View History

2021-02-11 21:31:41 +08:00
/*!
* 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.autocomplete;
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.autocomplete = 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.autocomplete.noConflict = function noConflict() {
$.fn.autocomplete = old;
return this;
};
$.fn.autocomplete.sources = Typeahead.sources;
$.fn.autocomplete.escapeHighlightedString = _.escapeHighlightedString;
module.exports = $.fn.autocomplete;
/***/ },
/* 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 || '<em>';
var pre = document.createElement('div');
pre.appendChild(document.createTextNode(highlightPreTag));
highlightPostTag = highlightPostTag || '</em>';
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('<pre aria-hidden="true"></pre>')
.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 <script> element; its readystatechange event will be fired asynchronously once it is inserted
// into the document. Do so, thus queuing up the task. Remember to clean up once it's been called.
var scriptEl = global.document.createElement('script');
scriptEl.onreadystatechange = function () {
handle();
scriptEl.onreadystatechange = null;
scriptEl.parentNode.removeChild(scriptEl);
scriptEl = null;
};
global.document.documentElement.appendChild(scriptEl);
return handle;
};
};
/* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }())))
/***/ },
/* 15 */
/***/ function(module, exports) {
'use strict';
exports.test = function () {
return true;
};
exports.install = function (t) {
return function () {
setTimeout(t, 0);
};
};
/***/ },
/* 16 */
/***/ function(module, exports, __webpack_require__) {
'use strict';
var _ = __webpack_require__(4);
var DOM = __webpack_require__(2);
var EventEmitter = __webpack_require__(8);
var Dataset = __webpack_require__(17);
var css = __webpack_require__(19);
// constructor
// -----------
function Dropdown(o) {
var that = this;
var onSuggestionClick;
var onSuggestionMouseEnter;
var onSuggestionMouseLeave;
o = o || {};
if (!o.menu) {
_.error('menu is required');
}
if (!_.isArray(o.datasets) && !_.isObject(o.datasets)) {
_.error('1 or more datasets required');
}
if (!o.datasets) {
_.error('datasets is required');
}
this.isOpen = false;
this.isEmpty = true;
this.minLength = o.minLength || 0;
this.templates = {};
this.appendTo = o.appendTo || false;
this.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);
// bound functions
onSuggestionClick = _.bind(this._onSuggestionClick, this);
onSuggestionMouseEnter = _.bind(this._onSuggestionMouseEnter, this);
onSuggestionMouseLeave = _.bind(this._onSuggestionMouseLeave, this);
var cssClass = _.className(this.cssClasses.prefix, this.cssClasses.suggestion);
this.$menu = DOM.element(o.menu)
.on('mouseenter.aa', cssClass, onSuggestionMouseEnter)
.on('mouseleave.aa', cssClass, onSuggestionMouseLeave)
.on('click.aa', cssClass, onSuggestionClick);
this.$container = o.appendTo ? o.wrapper : this.$menu;
if (o.templates && o.templates.header) {
this.templates.header = _.templatify(o.templates.header);
this.$menu.prepend(this.templates.header());
}
if (o.templates && o.templates.empty) {
this.templates.empty = _.templatify(o.templates.empty);
this.$empty = DOM.element('<div class="' +
_.className(this.cssClasses.prefix, this.cssClasses.empty, true) + '">' +
'</div>');
this.$menu.append(this.$empty);
this.$empty.hide();
}
this.datasets = _.map(o.datasets, function(oDataset) {
return initializeDataset(that.$menu, oDataset, o.cssClasses);
});
_.each(this.datasets, function(dataset) {
var root = dataset.getRoot();
if (root && root.parent().length === 0) {
that.$menu.append(root);
}
dataset.onSync('rendered', that._onRendered, that);
});
if (o.templates && o.templates.footer) {
this.templates.footer = _.templatify(o.templates.footer);
this.$menu.append(this.templates.footer());
}
var self = this;
DOM.element(window).resize(function() {
self._redraw();
});
}
// instance methods
// ----------------
_.mixin(Dropdown.prototype, EventEmitter, {
// ### private
_onSuggestionClick: function onSuggestionClick($e) {
this.trigger('suggestionClicked', DOM.element($e.currentTarget));
},
_onSuggestionMouseEnter: function onSuggestionMouseEnter($e) {
var elt = DOM.element($e.currentTarget);
if (elt.hasClass(_.className(this.cssClasses.prefix, this.cssClasses.cursor, true))) {
// we're already on the cursor
// => we're probably entering it again after leaving it for a nested div
return;
}
this._removeCursor();
// Fixes iOS double tap behaviour, by modifying the DOM right before the
// native href clicks happens, iOS will requires another tap to follow
// a suggestion that has an <a href> element inside
// https://www.google.com/search?q=ios+double+tap+bug+href
var suggestion = this;
setTimeout(function() {
// this exact line, when inside the main loop, will trigger a double tap bug
// on iOS devices
suggestion._setCursor(elt, false);
}, 0);
},
_onSuggestionMouseLeave: function onSuggestionMouseLeave($e) {
// $e.relatedTarget is the `EventTarget` the pointing device entered to
if ($e.relatedTarget) {
var elt = DOM.element($e.relatedTarget);
if (elt.closest('.' + _.className(this.cssClasses.prefix, this.cssClasses.cursor, true)).length > 0) {
// our father is a cursor
// => it means we're just leaving the suggestion for a nested div
return;
}
}
this._removeCursor();
this.trigger('cursorRemoved');
},
_onRendered: function onRendered(e, query) {
this.isEmpty = _.every(this.datasets, isDatasetEmpty);
if (this.isEmpty) {
if (query.length >= this.minLength) {
this.trigger('empty');
}
if (this.$empty) {
if (query.length < this.minLength) {
this._hide();
} else {
var html = this.templates.empty({
query: this.datasets[0] && this.datasets[0].query
});
this.$empty.html(html);
this.$empty.show();
this._show();
}
} else if (_.any(this.datasets, hasEmptyTemplate)) {
if (query.length < this.minLength) {
this._hide();
} else {
this._show();
}
} else {
this._hide();
}
} else if (this.isOpen) {
if (this.$empty) {
this.$empty.empty();
this.$empty.hide();
}
if (query.length >= this.minLength) {
this._show();
} else {
this._hide();
}
}
this.trigger('datasetRendered');
function isDatasetEmpty(dataset) {
return dataset.isEmpty();
}
function hasEmptyTemplate(dataset) {
return dataset.templates && dataset.templates.empty;
}
},
_hide: function() {
this.$container.hide();
},
_show: function() {
// can't use jQuery#show because $menu is a span element we want
// display: block; not dislay: inline;
this.$container.css('display', 'block');
this._redraw();
this.trigger('shown');
},
_redraw: function redraw() {
if (!this.isOpen || !this.appendTo) return;
this.trigger('redrawn');
},
_getSuggestions: function getSuggestions() {
return this.$menu.find(_.className(this.cssClasses.prefix, this.cssClasses.suggestion));
},
_getCursor: function getCursor() {
return this.$menu.find(_.className(this.cssClasses.prefix, this.cssClasses.cursor)).first();
},
_setCursor: function setCursor($el, updateInput) {
$el.first()
.addClass(_.className(this.cssClasses.prefix, this.cssClasses.cursor, true))
.attr('aria-selected', 'true');
this.trigger('cursorMoved', updateInput);
},
_removeCursor: function removeCursor() {
this._getCursor()
.removeClass(_.className(this.cssClasses.prefix, this.cssClasses.cursor, true))
.removeAttr('aria-selected');
},
_moveCursor: function moveCursor(increment) {
var $suggestions;
var $oldCursor;
var newCursorIndex;
var $newCursor;
if (!this.isOpen) {
return;
}
$oldCursor = this._getCursor();
$suggestions = this._getSuggestions();
this._removeCursor();
// shifting before and after modulo to deal with -1 index
newCursorIndex = $suggestions.index($oldCursor) + increment;
newCursorIndex = (newCursorIndex + 1) % ($suggestions.length + 1) - 1;
if (newCursorIndex === -1) {
this.trigger('cursorRemoved');
return;
} else if (newCursorIndex < -1) {
newCursorIndex = $suggestions.length - 1;
}
this._setCursor($newCursor = $suggestions.eq(newCursorIndex), true);
// in the case of scrollable overflow
// make sure the cursor is visible in the menu
this._ensureVisible($newCursor);
},
_ensureVisible: function ensureVisible($el) {
var elTop;
var elBottom;
var menuScrollTop;
var menuHeight;
elTop = $el.position().top;
elBottom = elTop + $el.height() +
parseInt($el.css('margin-top'), 10) +
parseInt($el.css('margin-bottom'), 10);
menuScrollTop = this.$menu.scrollTop();
menuHeight = this.$menu.height() +
parseInt(this.$menu.css('padding-top'), 10) +
parseInt(this.$menu.css('padding-bottom'), 10);
if (elTop < 0) {
this.$menu.scrollTop(menuScrollTop + elTop);
} else if (menuHeight < elBottom) {
this.$menu.scrollTop(menuScrollTop + (elBottom - menuHeight));
}
},
// ### public
close: function close() {
if (this.isOpen) {
this.isOpen = false;
this._removeCursor();
this._hide();
this.trigger('closed');
}
},
open: function open() {
if (!this.isOpen) {
this.isOpen = true;
if (!this.isEmpty) {
this._show();
}
this.trigger('opened');
}
},
setLanguageDirection: function setLanguageDirection(dir) {
this.$menu.css(dir === 'ltr' ? this.css.ltr : this.css.rtl);
},
moveCursorUp: function moveCursorUp() {
this._moveCursor(-1);
},
moveCursorDown: function moveCursorDown() {
this._moveCursor(+1);
},
getDatumForSuggestion: function getDatumForSuggestion($el) {
var datum = null;
if ($el.length) {
datum = {
raw: Dataset.extractDatum($el),
value: Dataset.extractValue($el),
datasetName: Dataset.extractDatasetName($el)
};
}
return datum;
},
getCurrentCursor: function getCurrentCursor() {
return this._getCursor().first();
},
getDatumForCursor: function getDatumForCursor() {
return this.getDatumForSuggestion(this._getCursor().first());
},
getDatumForTopSuggestion: function getDatumForTopSuggestion() {
return this.getDatumForSuggestion(this._getSuggestions().first());
},
cursorTopSuggestion: function cursorTopSuggestion() {
this._setCursor(this._getSuggestions().first(), false);
},
update: function update(query) {
_.each(this.datasets, updateDataset);
function updateDataset(dataset) {
dataset.update(query);
}
},
empty: function empty() {
_.each(this.datasets, clearDataset);
this.isEmpty = true;
function clearDataset(dataset) {
dataset.clear();
}
},
isVisible: function isVisible() {
return this.isOpen && !this.isEmpty;
},
destroy: function destroy() {
this.$menu.off('.aa');
this.$menu = null;
_.each(this.datasets, destroyDataset);
function destroyDataset(dataset) {
dataset.destroy();
}
}
});
// helper functions
// ----------------
Dropdown.Dataset = Dataset;
function initializeDataset($menu, oDataset, cssClasses) {
return new Dropdown.Dataset(_.mixin({$menu: $menu, cssClasses: cssClasses}, oDataset));
}
module.exports = Dropdown;
/***/ },
/* 17 */
/***/ function(module, exports, __webpack_require__) {
'use strict';
var datasetKey = 'aaDataset';
var valueKey = 'aaValue';
var datumKey = 'aaDatum';
var _ = __webpack_require__(4);
var DOM = __webpack_require__(2);
var html = __webpack_require__(18);
var css = __webpack_require__(19);
var EventEmitter = __webpack_require__(8);
// constructor
// -----------
function Dataset(o) {
o = o || {};
o.templates = o.templates || {};
if (!o.source) {
_.error('missing source');
}
if (o.name && !isValidName(o.name)) {
_.error('invalid dataset name: ' + o.name);
}
// tracks the last query the dataset was updated for
this.query = null;
this._isEmpty = true;
this.highlight = !!o.highlight;
this.name = typeof o.name === 'undefined' || o.name === null ? _.getUniqueId() : o.name;
this.source = o.source;
this.displayFn = getDisplayFn(o.display || o.displayKey);
this.debounce = o.debounce;
this.cache = o.cache !== false;
this.templates = getTemplates(o.templates, this.displayFn);
this.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);
var clazz = _.className(this.cssClasses.prefix, this.cssClasses.dataset);
this.$el = o.$menu && o.$menu.find(clazz + '-' + this.name).length > 0 ?
DOM.element(o.$menu.find(clazz + '-' + this.name)[0]) :
DOM.element(
html.dataset.replace('%CLASS%', this.name)
.replace('%PREFIX%', this.cssClasses.prefix)
.replace('%DATASET%', this.cssClasses.dataset)
);
this.$menu = o.$menu;
this.clearCachedSuggestions();
}
// static methods
// --------------
Dataset.extractDatasetName = function extractDatasetName(el) {
return DOM.element(el).data(datasetKey);
};
Dataset.extractValue = function extractValue(el) {
return DOM.element(el).data(valueKey);
};
Dataset.extractDatum = function extractDatum(el) {
var datum = DOM.element(el).data(datumKey);
if (typeof datum === 'string') {
// Zepto has an automatic deserialization of the
// JSON encoded data attribute
datum = JSON.parse(datum);
}
return datum;
};
// instance methods
// ----------------
_.mixin(Dataset.prototype, EventEmitter, {
// ### private
_render: function render(query, suggestions) {
if (!this.$el) {
return;
}
var that = this;
var hasSuggestions;
var renderArgs = [].slice.call(arguments, 2);
this.$el.empty();
hasSuggestions = suggestions && suggestions.length;
this._isEmpty = !hasSuggestions;
if (!hasSuggestions && this.templates.empty) {
this.$el
.html(getEmptyHtml.apply(this, renderArgs))
.prepend(that.templates.header ? getHeaderHtml.apply(this, renderArgs) : null)
.append(that.templates.footer ? getFooterHtml.apply(this, renderArgs) : null);
} else if (hasSuggestions) {
this.$el
.html(getSuggestionsHtml.apply(this, renderArgs))
.prepend(that.templates.header ? getHeaderHtml.apply(this, renderArgs) : null)
.append(that.templates.footer ? getFooterHtml.apply(this, renderArgs) : null);
} else if (suggestions && !Array.isArray(suggestions)) {
throw new TypeError('suggestions must be an array');
}
if (this.$menu) {
this.$menu.addClass(
this.cssClasses.prefix + (hasSuggestions ? 'with' : 'without') + '-' + this.name
).removeClass(
this.cssClasses.prefix + (hasSuggestions ? 'without' : 'with') + '-' + this.name
);
}
this.trigger('rendered', query);
function getEmptyHtml() {
var args = [].slice.call(arguments, 0);
args = [{query: query, isEmpty: true}].concat(args);
return that.templates.empty.apply(this, args);
}
function getSuggestionsHtml() {
var args = [].slice.call(arguments, 0);
var $suggestions;
var nodes;
var self = this;
var suggestionsHtml = html.suggestions.
replace('%PREFIX%', this.cssClasses.prefix).
replace('%SUGGESTIONS%', this.cssClasses.suggestions);
$suggestions = DOM
.element(suggestionsHtml)
.css(this.css.suggestions);
// jQuery#append doesn't support arrays as the first argument
// until version 1.8, see http://bugs.jquery.com/ticket/11231
nodes = _.map(suggestions, getSuggestionNode);
$suggestions.append.apply($suggestions, nodes);
return $suggestions;
function getSuggestionNode(suggestion) {
var $el;
var suggestionHtml = html.suggestion.
replace('%PREFIX%', self.cssClasses.prefix).
replace('%SUGGESTION%', self.cssClasses.suggestion);
$el = DOM.element(suggestionHtml)
.attr({
role: 'option',
id: ['option', Math.floor(Math.random() * 100000000)].join('-')
})
.append(that.templates.suggestion.apply(this, [suggestion].concat(args)));
$el.data(datasetKey, that.name);
$el.data(valueKey, that.displayFn(suggestion) || undefined); // this led to undefined return value
$el.data(datumKey, JSON.stringify(suggestion));
$el.children().each(function() { DOM.element(this).css(self.css.suggestionChild); });
return $el;
}
}
function getHeaderHtml() {
var args = [].slice.call(arguments, 0);
args = [{query: query, isEmpty: !hasSuggestions}].concat(args);
return that.templates.header.apply(this, args);
}
function getFooterHtml() {
var args = [].slice.call(arguments, 0);
args = [{query: query, isEmpty: !hasSuggestions}].concat(args);
return that.templates.footer.apply(this, args);
}
},
// ### public
getRoot: function getRoot() {
return this.$el;
},
update: function update(query) {
function handleSuggestions(suggestions) {
// if the update has been canceled or if the query has changed
// do not render the suggestions as they've become outdated
if (!this.canceled && query === this.query) {
// concat all the other arguments that could have been passed
// to the render function, and forward them to _render
var extraArgs = [].slice.call(arguments, 1);
this.cacheSuggestions(query, suggestions, extraArgs);
this._render.apply(this, [query, suggestions].concat(extraArgs));
}
}
this.query = query;
this.canceled = false;
if (this.shouldFetchFromCache(query)) {
handleSuggestions.apply(this, [this.cachedSuggestions].concat(this.cachedRenderExtraArgs));
} else {
var that = this;
var execSource = function() {
// When the call is debounced the condition avoid to do a useless
// request with the last character when the input has been cleared
if (!that.canceled) {
that.source(query, handleSuggestions.bind(that));
}
};
if (this.debounce) {
var later = function() {
that.debounceTimeout = null;
execSource();
};
clearTimeout(this.debounceTimeout);
this.debounceTimeout = setTimeout(later, this.debounce);
} else {
execSource();
}
}
},
cacheSuggestions: function cacheSuggestions(query, suggestions, extraArgs) {
this.cachedQuery = query;
this.cachedSuggestions = suggestions;
this.cachedRenderExtraArgs = extraArgs;
},
shouldFetchFromCache: function shouldFetchFromCache(query) {
return this.cache &&
this.cachedQuery === query &&
this.cachedSuggestions &&
this.cachedSuggestions.length;
},
clearCachedSuggestions: function clearCachedSuggestions() {
delete this.cachedQuery;
delete this.cachedSuggestions;
delete this.cachedRenderExtraArgs;
},
cancel: function cancel() {
this.canceled = true;
},
clear: function clear() {
this.cancel();
this.$el.empty();
this.trigger('rendered', '');
},
isEmpty: function isEmpty() {
return this._isEmpty;
},
destroy: function destroy() {
this.clearCachedSuggestions();
this.$el = null;
}
});
// helper functions
// ----------------
function getDisplayFn(display) {
display = display || 'value';
return _.isFunction(display) ? display : displayFn;
function displayFn(obj) {
return obj[display];
}
}
function getTemplates(templates, displayFn) {
return {
empty: templates.empty && _.templatify(templates.empty),
header: templates.header && _.templatify(templates.header),
footer: templates.footer && _.templatify(templates.footer),
suggestion: templates.suggestion || suggestionTemplate
};
function suggestionTemplate(context) {
return '<p>' + displayFn(context) + '</p>';
}
}
function isValidName(str) {
// dashes, underscores, letters, and numbers
return (/^[_a-zA-Z0-9-]+$/).test(str);
}
module.exports = Dataset;
/***/ },
/* 18 */
/***/ function(module, exports) {
'use strict';
module.exports = {
wrapper: '<span class="%ROOT%"></span>',
dropdown: '<span class="%PREFIX%%DROPDOWN_MENU%"></span>',
dataset: '<div class="%PREFIX%%DATASET%-%CLASS%"></div>',
suggestions: '<span class="%PREFIX%%SUGGESTIONS%"></span>',
suggestion: '<div class="%PREFIX%%SUGGESTION%"></div>'
};
/***/ },
/* 19 */
/***/ function(module, exports, __webpack_require__) {
'use strict';
var _ = __webpack_require__(4);
var css = {
wrapper: {
position: 'relative',
display: 'inline-block'
},
hint: {
position: 'absolute',
top: '0',
left: '0',
borderColor: 'transparent',
boxShadow: 'none',
// #741: fix hint opacity issue on iOS
opacity: '1'
},
input: {
position: 'relative',
verticalAlign: 'top',
backgroundColor: 'transparent'
},
inputWithNoHint: {
position: 'relative',
verticalAlign: 'top'
},
dropdown: {
position: 'absolute',
top: '100%',
left: '0',
zIndex: '100',
display: 'none'
},
suggestions: {
display: 'block'
},
suggestion: {
whiteSpace: 'nowrap',
cursor: 'pointer'
},
suggestionChild: {
whiteSpace: 'normal'
},
ltr: {
left: '0',
right: 'auto'
},
rtl: {
left: 'auto',
right: '0'
},
defaultClasses: {
root: 'algolia-autocomplete',
prefix: 'aa',
noPrefix: false,
dropdownMenu: 'dropdown-menu',
input: 'input',
hint: 'hint',
suggestions: 'suggestions',
suggestion: 'suggestion',
cursor: 'cursor',
dataset: 'dataset',
empty: 'empty'
},
// will be merged with the default ones if appendTo is used
appendTo: {
wrapper: {
position: 'absolute',
zIndex: '100',
display: 'none'
},
input: {},
inputWithNoHint: {},
dropdown: {
display: 'block'
}
}
};
// ie specific styling
if (_.isMsie()) {
// ie6-8 (and 9?) doesn't fire hover and click events for elements with
// transparent backgrounds, for a workaround, use 1x1 transparent gif
_.mixin(css.input, {
backgroundImage: 'url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)'
});
}
// ie7 and under specific styling
if (_.isMsie() && _.isMsie() <= 7) {
// if someone can tell me why this is necessary to align
// the hint with the query in ie7, i'll send you $5 - @JakeHarding
_.mixin(css.input, {marginTop: '-1px'});
}
module.exports = css;
/***/ },
/* 20 */
/***/ function(module, exports, __webpack_require__) {
'use strict';
module.exports = {
hits: __webpack_require__(21),
popularIn: __webpack_require__(24)
};
/***/ },
/* 21 */
/***/ function(module, exports, __webpack_require__) {
'use strict';
var _ = __webpack_require__(4);
var version = __webpack_require__(22);
var parseAlgoliaClientVersion = __webpack_require__(23);
module.exports = function search(index, params) {
var algoliaVersion = parseAlgoliaClientVersion(index.as._ua);
if (algoliaVersion && algoliaVersion[0] >= 3 && algoliaVersion[1] > 20) {
params = params || {};
params.additionalUA = 'autocomplete.js ' + version;
}
return sourceFn;
function sourceFn(query, cb) {
index.search(query, params, function(error, content) {
if (error) {
_.error(error.message);
return;
}
cb(content.hits, content);
});
}
};
/***/ },
/* 22 */
/***/ function(module, exports) {
module.exports = "0.36.0";
/***/ },
/* 23 */
/***/ function(module, exports) {
'use strict';
module.exports = function parseAlgoliaClientVersion(agent) {
var parsed = agent.match(/Algolia for vanilla JavaScript (\d+\.)(\d+\.)(\d+)/);
if (parsed) return [parsed[1], parsed[2], parsed[3]];
return undefined;
};
/***/ },
/* 24 */
/***/ function(module, exports, __webpack_require__) {
'use strict';
var _ = __webpack_require__(4);
var version = __webpack_require__(22);
var parseAlgoliaClientVersion = __webpack_require__(23);
module.exports = function popularIn(index, params, details, options) {
var algoliaVersion = parseAlgoliaClientVersion(index.as._ua);
if (algoliaVersion && algoliaVersion[0] >= 3 && algoliaVersion[1] > 20) {
params = params || {};
params.additionalUA = 'autocomplete.js ' + version;
}
if (!details.source) {
return _.error("Missing 'source' key");
}
var source = _.isFunction(details.source) ? details.source : function(hit) { return hit[details.source]; };
if (!details.index) {
return _.error("Missing 'index' key");
}
var detailsIndex = details.index;
options = options || {};
return sourceFn;
function sourceFn(query, cb) {
index.search(query, params, function(error, content) {
if (error) {
_.error(error.message);
return;
}
if (content.hits.length > 0) {
var first = content.hits[0];
var detailsParams = _.mixin({hitsPerPage: 0}, details);
delete detailsParams.source; // not a query parameter
delete detailsParams.index; // not a query parameter
var detailsAlgoliaVersion = parseAlgoliaClientVersion(detailsIndex.as._ua);
if (detailsAlgoliaVersion && detailsAlgoliaVersion[0] >= 3 && detailsAlgoliaVersion[1] > 20) {
params.additionalUA = 'autocomplete.js ' + version;
}
detailsIndex.search(source(first), detailsParams, function(error2, content2) {
if (error2) {
_.error(error2.message);
return;
}
var suggestions = [];
// add the 'all department' entry before others
if (options.includeAll) {
var label = options.allTitle || 'All departments';
suggestions.push(_.mixin({
facet: {value: label, count: content2.nbHits}
}, _.cloneDeep(first)));
}
// enrich the first hit iterating over the facets
_.each(content2.facets, function(values, facet) {
_.each(values, function(count, value) {
suggestions.push(_.mixin({
facet: {facet: facet, value: value, count: count}
}, _.cloneDeep(first)));
});
});
// append all other hits
for (var i = 1; i < content.hits.length; ++i) {
suggestions.push(content.hits[i]);
}
cb(suggestions, content);
});
return;
}
cb([]);
});
}
};
/***/ }
/******/ ]);