292 lines
7.4 KiB
JavaScript
292 lines
7.4 KiB
JavaScript
/*
|
|
* typeahead.js
|
|
* https://github.com/twitter/typeahead.js
|
|
* Copyright 2013-2014 Twitter, Inc. and other contributors; Licensed MIT
|
|
*/
|
|
|
|
(function() {
|
|
'use strict';
|
|
|
|
var old, keys, methods;
|
|
|
|
old = $.fn.typeahead;
|
|
|
|
keys = {
|
|
www: 'tt-www',
|
|
attrs: 'tt-attrs',
|
|
typeahead: 'tt-typeahead'
|
|
};
|
|
|
|
methods = {
|
|
// supported signatures:
|
|
// function(o, dataset, dataset, ...)
|
|
// function(o, [dataset, dataset, ...])
|
|
initialize: function initialize(o, datasets) {
|
|
var www;
|
|
|
|
datasets = _.isArray(datasets) ? datasets : [].slice.call(arguments, 1);
|
|
|
|
o = o || {};
|
|
www = WWW(o.classNames);
|
|
|
|
return this.each(attach);
|
|
|
|
function attach() {
|
|
var $input, $wrapper, $hint, $menu, defaultHint, defaultMenu,
|
|
eventBus, input, menu, typeahead, MenuConstructor;
|
|
|
|
// highlight is a top-level config that needs to get inherited
|
|
// from all of the datasets
|
|
_.each(datasets, function(d) { d.highlight = !!o.highlight; });
|
|
|
|
$input = $(this);
|
|
$wrapper = $(www.html.wrapper);
|
|
$hint = $elOrNull(o.hint);
|
|
$menu = $elOrNull(o.menu);
|
|
|
|
defaultHint = o.hint !== false && !$hint;
|
|
defaultMenu = o.menu !== false && !$menu;
|
|
|
|
defaultHint && ($hint = buildHintFromInput($input, www));
|
|
defaultMenu && ($menu = $(www.html.menu).css(www.css.menu));
|
|
|
|
// hint should be empty on init
|
|
$hint && $hint.val('');
|
|
$input = prepInput($input, www);
|
|
|
|
// only apply inline styles and make dom changes if necessary
|
|
if (defaultHint || defaultMenu) {
|
|
$wrapper.css(www.css.wrapper);
|
|
$input.css(defaultHint ? www.css.input : www.css.inputWithNoHint);
|
|
|
|
$input
|
|
.wrap($wrapper)
|
|
.parent()
|
|
.prepend(defaultHint ? $hint : null)
|
|
.append(defaultMenu ? $menu : null);
|
|
}
|
|
|
|
MenuConstructor = defaultMenu ? DefaultMenu : Menu;
|
|
|
|
eventBus = new EventBus({ el: $input });
|
|
input = new Input({ hint: $hint, input: $input, }, www);
|
|
menu = new MenuConstructor({
|
|
node: $menu,
|
|
datasets: datasets
|
|
}, www);
|
|
|
|
typeahead = new Typeahead({
|
|
input: input,
|
|
menu: menu,
|
|
eventBus: eventBus,
|
|
minLength: o.minLength
|
|
}, www);
|
|
|
|
$input.data(keys.www, www);
|
|
$input.data(keys.typeahead, typeahead);
|
|
}
|
|
},
|
|
|
|
isEnabled: function isEnabled() {
|
|
var enabled;
|
|
|
|
ttEach(this.first(), function(t) { enabled = t.isEnabled(); });
|
|
return enabled;
|
|
},
|
|
|
|
enable: function enable() {
|
|
ttEach(this, function(t) { t.enable(); });
|
|
return this;
|
|
},
|
|
|
|
disable: function disable() {
|
|
ttEach(this, function(t) { t.disable(); });
|
|
return this;
|
|
},
|
|
|
|
isActive: function isActive() {
|
|
var active;
|
|
|
|
ttEach(this.first(), function(t) { active = t.isActive(); });
|
|
return active;
|
|
},
|
|
|
|
activate: function activate() {
|
|
ttEach(this, function(t) { t.activate(); });
|
|
return this;
|
|
},
|
|
|
|
deactivate: function deactivate() {
|
|
ttEach(this, function(t) { t.deactivate(); });
|
|
return this;
|
|
},
|
|
|
|
isOpen: function isOpen() {
|
|
var open;
|
|
|
|
ttEach(this.first(), function(t) { open = t.isOpen(); });
|
|
return open;
|
|
},
|
|
|
|
open: function open() {
|
|
ttEach(this, function(t) { t.open(); });
|
|
return this;
|
|
},
|
|
|
|
close: function close() {
|
|
ttEach(this, function(t) { t.close(); });
|
|
return this;
|
|
},
|
|
|
|
select: function select(el) {
|
|
var success = false, $el = $(el);
|
|
|
|
ttEach(this.first(), function(t) { success = t.select($el); });
|
|
return success;
|
|
},
|
|
|
|
autocomplete: function autocomplete(el) {
|
|
var success = false, $el = $(el);
|
|
|
|
ttEach(this.first(), function(t) { success = t.autocomplete($el); });
|
|
return success;
|
|
},
|
|
|
|
moveCursor: function moveCursoe(delta) {
|
|
var success = false;
|
|
|
|
ttEach(this.first(), function(t) { success = t.moveCursor(delta); });
|
|
return success;
|
|
},
|
|
|
|
// mirror jQuery#val functionality: reads operate on first match,
|
|
// write operates on all matches
|
|
val: function val(newVal) {
|
|
var query;
|
|
|
|
if (!arguments.length) {
|
|
ttEach(this.first(), function(t) { query = t.getVal(); });
|
|
return query;
|
|
}
|
|
|
|
else {
|
|
ttEach(this, function(t) { t.setVal(_.toStr(newVal)); });
|
|
return this;
|
|
}
|
|
},
|
|
|
|
destroy: function destroy() {
|
|
ttEach(this, function(typeahead, $input) {
|
|
revert($input);
|
|
typeahead.destroy();
|
|
});
|
|
|
|
return this;
|
|
}
|
|
};
|
|
|
|
$.fn.typeahead = function(method) {
|
|
// methods that should only act on initialized typeaheads
|
|
if (methods[method]) {
|
|
return methods[method].apply(this, [].slice.call(arguments, 1));
|
|
}
|
|
|
|
else {
|
|
return methods.initialize.apply(this, arguments);
|
|
}
|
|
};
|
|
|
|
$.fn.typeahead.noConflict = function noConflict() {
|
|
$.fn.typeahead = old;
|
|
return this;
|
|
};
|
|
|
|
// helper methods
|
|
// --------------
|
|
|
|
function ttEach($els, fn) {
|
|
$els.each(function() {
|
|
var $input = $(this), typeahead;
|
|
|
|
(typeahead = $input.data(keys.typeahead)) && fn(typeahead, $input);
|
|
});
|
|
}
|
|
|
|
function buildHintFromInput($input, www) {
|
|
return $input.clone()
|
|
.addClass(www.classes.hint)
|
|
.removeData()
|
|
.css(www.css.hint)
|
|
.css(getBackgroundStyles($input))
|
|
.prop('readonly', true)
|
|
.removeAttr('id name placeholder required')
|
|
.attr({ autocomplete: 'off', spellcheck: 'false', tabindex: -1 });
|
|
}
|
|
|
|
function prepInput($input, www) {
|
|
// store the original values of the attrs that get modified
|
|
// so modifications can be reverted on destroy
|
|
$input.data(keys.attrs, {
|
|
dir: $input.attr('dir'),
|
|
autocomplete: $input.attr('autocomplete'),
|
|
spellcheck: $input.attr('spellcheck'),
|
|
style: $input.attr('style')
|
|
});
|
|
|
|
$input
|
|
.addClass(www.classes.input)
|
|
.attr({ autocomplete: 'off', spellcheck: false });
|
|
|
|
// ie7 does not like it when dir is set to auto
|
|
try { !$input.attr('dir') && $input.attr('dir', 'auto'); } catch (e) {}
|
|
|
|
return $input;
|
|
}
|
|
|
|
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 revert($input) {
|
|
var www, $wrapper;
|
|
|
|
www = $input.data(keys.www);
|
|
$wrapper = $input.parent().filter(www.selectors.wrapper);
|
|
|
|
// need to remove attrs that weren't previously defined and
|
|
// revert attrs that originally had a value
|
|
_.each($input.data(keys.attrs), function(val, key) {
|
|
_.isUndefined(val) ? $input.removeAttr(key) : $input.attr(key, val);
|
|
});
|
|
|
|
$input
|
|
.removeData(keys.typeahead)
|
|
.removeData(keys.www)
|
|
.removeData(keys.attr)
|
|
.removeClass(www.classes.input);
|
|
|
|
if ($wrapper.length) {
|
|
$input.detach().insertAfter($wrapper);
|
|
$wrapper.remove();
|
|
}
|
|
}
|
|
|
|
function $elOrNull(obj) {
|
|
var isValid, $el;
|
|
|
|
isValid = _.isJQuery(obj) || _.isElement(obj);
|
|
$el = isValid ? $(obj).first() : [];
|
|
|
|
return $el.length ? $el : null;
|
|
}
|
|
})();
|