|
@@ -1,18 +1,18 @@
|
|
/*!
|
|
/*!
|
|
- * typeahead.js 0.11.1
|
|
|
|
|
|
+ * typeahead.js 1.2.0
|
|
* https://github.com/twitter/typeahead.js
|
|
* https://github.com/twitter/typeahead.js
|
|
- * Copyright 2013-2015 Twitter, Inc. and other contributors; Licensed MIT
|
|
|
|
|
|
+ * Copyright 2013-2017 Twitter, Inc. and other contributors; Licensed MIT
|
|
*/
|
|
*/
|
|
|
|
|
|
(function(root, factory) {
|
|
(function(root, factory) {
|
|
if (typeof define === "function" && define.amd) {
|
|
if (typeof define === "function" && define.amd) {
|
|
- define("typeahead.js", [ "jquery" ], function(a0) {
|
|
|
|
|
|
+ define([ "jquery" ], function(a0) {
|
|
return factory(a0);
|
|
return factory(a0);
|
|
});
|
|
});
|
|
} else if (typeof exports === "object") {
|
|
} else if (typeof exports === "object") {
|
|
module.exports = factory(require("jquery"));
|
|
module.exports = factory(require("jquery"));
|
|
} else {
|
|
} else {
|
|
- factory(jQuery);
|
|
|
|
|
|
+ factory(root["jQuery"]);
|
|
}
|
|
}
|
|
})(this, function($) {
|
|
})(this, function($) {
|
|
var _ = function() {
|
|
var _ = function() {
|
|
@@ -148,6 +148,13 @@
|
|
stringify: function(val) {
|
|
stringify: function(val) {
|
|
return _.isString(val) ? val : JSON.stringify(val);
|
|
return _.isString(val) ? val : JSON.stringify(val);
|
|
},
|
|
},
|
|
|
|
+ guid: function() {
|
|
|
|
+ function _p8(s) {
|
|
|
|
+ var p = (Math.random().toString(16) + "000000000").substr(2, 8);
|
|
|
|
+ return s ? "-" + p.substr(0, 4) + "-" + p.substr(4, 4) : p;
|
|
|
|
+ }
|
|
|
|
+ return "tt-" + _p8() + _p8(true) + _p8(true) + _p8();
|
|
|
|
+ },
|
|
noop: function() {}
|
|
noop: function() {}
|
|
};
|
|
};
|
|
}();
|
|
}();
|
|
@@ -189,7 +196,7 @@
|
|
function buildHtml(c) {
|
|
function buildHtml(c) {
|
|
return {
|
|
return {
|
|
wrapper: '<span class="' + c.wrapper + '"></span>',
|
|
wrapper: '<span class="' + c.wrapper + '"></span>',
|
|
- menu: '<div class="' + c.menu + '"></div>'
|
|
|
|
|
|
+ menu: '<div role="listbox" class="' + c.menu + '"></div>'
|
|
};
|
|
};
|
|
}
|
|
}
|
|
function buildSelectors(classes) {
|
|
function buildSelectors(classes) {
|
|
@@ -264,10 +271,8 @@
|
|
}
|
|
}
|
|
_.mixin(EventBus.prototype, {
|
|
_.mixin(EventBus.prototype, {
|
|
_trigger: function(type, args) {
|
|
_trigger: function(type, args) {
|
|
- var $e;
|
|
|
|
- $e = $.Event(namespace + type);
|
|
|
|
- (args = args || []).unshift($e);
|
|
|
|
- this.$el.trigger.apply(this.$el, args);
|
|
|
|
|
|
+ var $e = $.Event(namespace + type);
|
|
|
|
+ this.$el.trigger.call(this.$el, $e, args || []);
|
|
return $e;
|
|
return $e;
|
|
},
|
|
},
|
|
before: function(type) {
|
|
before: function(type) {
|
|
@@ -384,7 +389,36 @@
|
|
tagName: "strong",
|
|
tagName: "strong",
|
|
className: null,
|
|
className: null,
|
|
wordsOnly: false,
|
|
wordsOnly: false,
|
|
- caseSensitive: false
|
|
|
|
|
|
+ caseSensitive: false,
|
|
|
|
+ diacriticInsensitive: false
|
|
|
|
+ };
|
|
|
|
+ var accented = {
|
|
|
|
+ A: "[AaªÀ-Åà-åĀ-ąǍǎȀ-ȃȦȧᴬᵃḀḁẚẠ-ảₐ℀℁℻⒜Ⓐⓐ㍱-㍴㎀-㎄㎈㎉㎩-㎯㏂㏊㏟㏿Aa]",
|
|
|
|
+ B: "[BbᴮᵇḂ-ḇℬ⒝Ⓑⓑ㍴㎅-㎇㏃㏈㏔㏝Bb]",
|
|
|
|
+ C: "[CcÇçĆ-čᶜ℀ℂ℃℅℆ℭⅭⅽ⒞Ⓒⓒ㍶㎈㎉㎝㎠㎤㏄-㏇Cc]",
|
|
|
|
+ D: "[DdĎďDŽ-džDZ-dzᴰᵈḊ-ḓⅅⅆⅮⅾ⒟Ⓓⓓ㋏㍲㍷-㍹㎗㎭-㎯㏅㏈Dd]",
|
|
|
|
+ E: "[EeÈ-Ëè-ëĒ-ěȄ-ȇȨȩᴱᵉḘ-ḛẸ-ẽₑ℡ℯℰⅇ⒠Ⓔⓔ㉐㋍㋎Ee]",
|
|
|
|
+ F: "[FfᶠḞḟ℉ℱ℻⒡Ⓕⓕ㎊-㎌㎙ff-fflFf]",
|
|
|
|
+ G: "[GgĜ-ģǦǧǴǵᴳᵍḠḡℊ⒢Ⓖⓖ㋌㋍㎇㎍-㎏㎓㎬㏆㏉㏒㏿Gg]",
|
|
|
|
+ H: "[HhĤĥȞȟʰᴴḢ-ḫẖℋ-ℎ⒣Ⓗⓗ㋌㍱㎐-㎔㏊㏋㏗Hh]",
|
|
|
|
+ I: "[IiÌ-Ïì-ïĨ-İIJijǏǐȈ-ȋᴵᵢḬḭỈ-ịⁱℐℑℹⅈⅠ-ⅣⅥ-ⅨⅪⅫⅰ-ⅳⅵ-ⅸⅺⅻ⒤Ⓘⓘ㍺㏌㏕fiffiIi]",
|
|
|
|
+ J: "[JjIJ-ĵLJ-njǰʲᴶⅉ⒥ⒿⓙⱼJj]",
|
|
|
|
+ K: "[KkĶķǨǩᴷᵏḰ-ḵK⒦Ⓚⓚ㎄㎅㎉㎏㎑㎘㎞㎢㎦㎪㎸㎾㏀㏆㏍-㏏Kk]",
|
|
|
|
+ L: "[LlĹ-ŀLJ-ljˡᴸḶḷḺ-ḽℒℓ℡Ⅼⅼ⒧Ⓛⓛ㋏㎈㎉㏐-㏓㏕㏖㏿flfflLl]",
|
|
|
|
+ M: "[MmᴹᵐḾ-ṃ℠™ℳⅯⅿ⒨Ⓜⓜ㍷-㍹㎃㎆㎎㎒㎖㎙-㎨㎫㎳㎷㎹㎽㎿㏁㏂㏎㏐㏔-㏖㏘㏙㏞㏟Mm]",
|
|
|
|
+ N: "[NnÑñŃ-ʼnNJ-njǸǹᴺṄ-ṋⁿℕ№⒩Ⓝⓝ㎁㎋㎚㎱㎵㎻㏌㏑Nn]",
|
|
|
|
+ O: "[OoºÒ-Öò-öŌ-őƠơǑǒǪǫȌ-ȏȮȯᴼᵒỌ-ỏₒ℅№ℴ⒪Ⓞⓞ㍵㏇㏒㏖Oo]",
|
|
|
|
+ P: "[PpᴾᵖṔ-ṗℙ⒫Ⓟⓟ㉐㍱㍶㎀㎊㎩-㎬㎰㎴㎺㏋㏗-㏚Pp]",
|
|
|
|
+ Q: "[Qqℚ⒬Ⓠⓠ㏃Qq]",
|
|
|
|
+ R: "[RrŔ-řȐ-ȓʳᴿᵣṘ-ṛṞṟ₨ℛ-ℝ⒭Ⓡⓡ㋍㍴㎭-㎯㏚㏛Rr]",
|
|
|
|
+ S: "[SsŚ-šſȘșˢṠ-ṣ₨℁℠⒮Ⓢⓢ㎧㎨㎮-㎳㏛㏜stSs]",
|
|
|
|
+ T: "[TtŢ-ťȚțᵀᵗṪ-ṱẗ℡™⒯Ⓣⓣ㉐㋏㎔㏏ſtstTt]",
|
|
|
|
+ U: "[UuÙ-Üù-üŨ-ųƯưǓǔȔ-ȗᵁᵘᵤṲ-ṷỤ-ủ℆⒰Ⓤⓤ㍳㍺Uu]",
|
|
|
|
+ V: "[VvᵛᵥṼ-ṿⅣ-Ⅷⅳ-ⅷ⒱Ⓥⓥⱽ㋎㍵㎴-㎹㏜㏞Vv]",
|
|
|
|
+ W: "[WwŴŵʷᵂẀ-ẉẘ⒲Ⓦⓦ㎺-㎿㏝Ww]",
|
|
|
|
+ X: "[XxˣẊ-ẍₓ℻Ⅸ-Ⅻⅸ-ⅻ⒳Ⓧⓧ㏓Xx]",
|
|
|
|
+ Y: "[YyÝýÿŶ-ŸȲȳʸẎẏẙỲ-ỹ⒴Ⓨⓨ㏉Yy]",
|
|
|
|
+ Z: "[ZzŹ-žDZ-dzᶻẐ-ẕℤℨ⒵Ⓩⓩ㎐-㎔Zz]"
|
|
};
|
|
};
|
|
return function hightlight(o) {
|
|
return function hightlight(o) {
|
|
var regex;
|
|
var regex;
|
|
@@ -393,7 +427,7 @@
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
o.pattern = _.isArray(o.pattern) ? o.pattern : [ o.pattern ];
|
|
o.pattern = _.isArray(o.pattern) ? o.pattern : [ o.pattern ];
|
|
- regex = getRegex(o.pattern, o.caseSensitive, o.wordsOnly);
|
|
|
|
|
|
+ regex = getRegex(o.pattern, o.caseSensitive, o.wordsOnly, o.diacriticInsensitive);
|
|
traverse(o.node, hightlightTextNode);
|
|
traverse(o.node, hightlightTextNode);
|
|
function hightlightTextNode(textNode) {
|
|
function hightlightTextNode(textNode) {
|
|
var match, patternNode, wrapperNode;
|
|
var match, patternNode, wrapperNode;
|
|
@@ -419,10 +453,17 @@
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
};
|
|
- function getRegex(patterns, caseSensitive, wordsOnly) {
|
|
|
|
|
|
+ function accent_replacer(chr) {
|
|
|
|
+ return accented[chr.toUpperCase()] || chr;
|
|
|
|
+ }
|
|
|
|
+ function getRegex(patterns, caseSensitive, wordsOnly, diacriticInsensitive) {
|
|
var escapedPatterns = [], regexStr;
|
|
var escapedPatterns = [], regexStr;
|
|
for (var i = 0, len = patterns.length; i < len; i++) {
|
|
for (var i = 0, len = patterns.length; i < len; i++) {
|
|
- escapedPatterns.push(_.escapeRegExChars(patterns[i]));
|
|
|
|
|
|
+ var escapedWord = _.escapeRegExChars(patterns[i]);
|
|
|
|
+ if (diacriticInsensitive) {
|
|
|
|
+ escapedWord = escapedWord.replace(/\S/g, accent_replacer);
|
|
|
|
+ }
|
|
|
|
+ escapedPatterns.push(escapedWord);
|
|
}
|
|
}
|
|
regexStr = wordsOnly ? "\\b(" + escapedPatterns.join("|") + ")\\b" : "(" + escapedPatterns.join("|") + ")";
|
|
regexStr = wordsOnly ? "\\b(" + escapedPatterns.join("|") + ")\\b" : "(" + escapedPatterns.join("|") + ")";
|
|
return caseSensitive ? new RegExp(regexStr) : new RegExp(regexStr, "i");
|
|
return caseSensitive ? new RegExp(regexStr) : new RegExp(regexStr, "i");
|
|
@@ -448,6 +489,14 @@
|
|
www.mixin(this);
|
|
www.mixin(this);
|
|
this.$hint = $(o.hint);
|
|
this.$hint = $(o.hint);
|
|
this.$input = $(o.input);
|
|
this.$input = $(o.input);
|
|
|
|
+ this.$input.attr({
|
|
|
|
+ "aria-activedescendant": "",
|
|
|
|
+ "aria-owns": this.$input.attr("id") + "_listbox",
|
|
|
|
+ role: "combobox",
|
|
|
|
+ "aria-readonly": "true",
|
|
|
|
+ "aria-autocomplete": "list"
|
|
|
|
+ });
|
|
|
|
+ $(www.menu).attr("id", this.$input.attr("id") + "_listbox");
|
|
this.query = this.$input.val();
|
|
this.query = this.$input.val();
|
|
this.queryWhenFocused = this.hasFocus() ? this.query : null;
|
|
this.queryWhenFocused = this.hasFocus() ? this.query : null;
|
|
this.$overflowHelper = buildOverflowHelper(this.$input);
|
|
this.$overflowHelper = buildOverflowHelper(this.$input);
|
|
@@ -455,6 +504,7 @@
|
|
if (this.$hint.length === 0) {
|
|
if (this.$hint.length === 0) {
|
|
this.setHint = this.getHint = this.clearHint = this.clearHintIfInvalid = _.noop;
|
|
this.setHint = this.getHint = this.clearHint = this.clearHintIfInvalid = _.noop;
|
|
}
|
|
}
|
|
|
|
+ this.onSync("cursorchange", this._updateDescendent);
|
|
}
|
|
}
|
|
Input.normalizeQuery = function(str) {
|
|
Input.normalizeQuery = function(str) {
|
|
return _.toStr(str).replace(/^\s*/g, "").replace(/\s{2,}/g, " ");
|
|
return _.toStr(str).replace(/^\s*/g, "").replace(/\s{2,}/g, " ");
|
|
@@ -524,6 +574,9 @@
|
|
this.trigger("whitespaceChanged", this.query);
|
|
this.trigger("whitespaceChanged", this.query);
|
|
}
|
|
}
|
|
},
|
|
},
|
|
|
|
+ _updateDescendent: function updateDescendent(event, id) {
|
|
|
|
+ this.$input.attr("aria-activedescendant", id);
|
|
|
|
+ },
|
|
bind: function() {
|
|
bind: function() {
|
|
var that = this, onBlur, onFocus, onKeydown, onInput;
|
|
var that = this, onBlur, onFocus, onKeydown, onInput;
|
|
onBlur = _.bind(this._onBlur, this);
|
|
onBlur = _.bind(this._onBlur, this);
|
|
@@ -647,6 +700,7 @@
|
|
"use strict";
|
|
"use strict";
|
|
var keys, nameGenerator;
|
|
var keys, nameGenerator;
|
|
keys = {
|
|
keys = {
|
|
|
|
+ dataset: "tt-selectable-dataset",
|
|
val: "tt-selectable-display",
|
|
val: "tt-selectable-display",
|
|
obj: "tt-selectable-object"
|
|
obj: "tt-selectable-object"
|
|
};
|
|
};
|
|
@@ -666,19 +720,20 @@
|
|
}
|
|
}
|
|
www.mixin(this);
|
|
www.mixin(this);
|
|
this.highlight = !!o.highlight;
|
|
this.highlight = !!o.highlight;
|
|
- this.name = o.name || nameGenerator();
|
|
|
|
|
|
+ this.name = _.toStr(o.name || nameGenerator());
|
|
this.limit = o.limit || 5;
|
|
this.limit = o.limit || 5;
|
|
this.displayFn = getDisplayFn(o.display || o.displayKey);
|
|
this.displayFn = getDisplayFn(o.display || o.displayKey);
|
|
this.templates = getTemplates(o.templates, this.displayFn);
|
|
this.templates = getTemplates(o.templates, this.displayFn);
|
|
this.source = o.source.__ttAdapter ? o.source.__ttAdapter() : o.source;
|
|
this.source = o.source.__ttAdapter ? o.source.__ttAdapter() : o.source;
|
|
this.async = _.isUndefined(o.async) ? this.source.length > 2 : !!o.async;
|
|
this.async = _.isUndefined(o.async) ? this.source.length > 2 : !!o.async;
|
|
this._resetLastSuggestion();
|
|
this._resetLastSuggestion();
|
|
- this.$el = $(o.node).addClass(this.classes.dataset).addClass(this.classes.dataset + "-" + this.name);
|
|
|
|
|
|
+ this.$el = $(o.node).attr("role", "presentation").addClass(this.classes.dataset).addClass(this.classes.dataset + "-" + this.name);
|
|
}
|
|
}
|
|
Dataset.extractData = function extractData(el) {
|
|
Dataset.extractData = function extractData(el) {
|
|
var $el = $(el);
|
|
var $el = $(el);
|
|
if ($el.data(keys.obj)) {
|
|
if ($el.data(keys.obj)) {
|
|
return {
|
|
return {
|
|
|
|
+ dataset: $el.data(keys.dataset) || "",
|
|
val: $el.data(keys.val) || "",
|
|
val: $el.data(keys.val) || "",
|
|
obj: $el.data(keys.obj) || null
|
|
obj: $el.data(keys.obj) || null
|
|
};
|
|
};
|
|
@@ -697,7 +752,7 @@
|
|
} else {
|
|
} else {
|
|
this._empty();
|
|
this._empty();
|
|
}
|
|
}
|
|
- this.trigger("rendered", this.name, suggestions, false);
|
|
|
|
|
|
+ this.trigger("rendered", suggestions, false, this.name);
|
|
},
|
|
},
|
|
_append: function append(query, suggestions) {
|
|
_append: function append(query, suggestions) {
|
|
suggestions = suggestions || [];
|
|
suggestions = suggestions || [];
|
|
@@ -708,7 +763,7 @@
|
|
} else if (!this.$lastSuggestion.length && this.templates.notFound) {
|
|
} else if (!this.$lastSuggestion.length && this.templates.notFound) {
|
|
this._renderNotFound(query);
|
|
this._renderNotFound(query);
|
|
}
|
|
}
|
|
- this.trigger("rendered", this.name, suggestions, true);
|
|
|
|
|
|
+ this.trigger("rendered", suggestions, true, this.name);
|
|
},
|
|
},
|
|
_renderSuggestions: function renderSuggestions(query, suggestions) {
|
|
_renderSuggestions: function renderSuggestions(query, suggestions) {
|
|
var $fragment;
|
|
var $fragment;
|
|
@@ -749,7 +804,7 @@
|
|
_.each(suggestions, function getSuggestionNode(suggestion) {
|
|
_.each(suggestions, function getSuggestionNode(suggestion) {
|
|
var $el, context;
|
|
var $el, context;
|
|
context = that._injectQuery(query, suggestion);
|
|
context = that._injectQuery(query, suggestion);
|
|
- $el = $(that.templates.suggestion(context)).data(keys.obj, suggestion).data(keys.val, that.displayFn(suggestion)).addClass(that.classes.suggestion + " " + that.classes.selectable);
|
|
|
|
|
|
+ $el = $(that.templates.suggestion(context)).data(keys.dataset, that.name).data(keys.obj, suggestion).data(keys.val, that.displayFn(suggestion)).addClass(that.classes.suggestion + " " + that.classes.selectable);
|
|
fragment.appendChild($el[0]);
|
|
fragment.appendChild($el[0]);
|
|
});
|
|
});
|
|
this.highlight && highlight({
|
|
this.highlight && highlight({
|
|
@@ -787,7 +842,7 @@
|
|
this.cancel = function cancel() {
|
|
this.cancel = function cancel() {
|
|
canceled = true;
|
|
canceled = true;
|
|
that.cancel = $.noop;
|
|
that.cancel = $.noop;
|
|
- that.async && that.trigger("asyncCanceled", query);
|
|
|
|
|
|
+ that.async && that.trigger("asyncCanceled", query, that.name);
|
|
};
|
|
};
|
|
this.source(query, sync, async);
|
|
this.source(query, sync, async);
|
|
!syncCalled && sync([]);
|
|
!syncCalled && sync([]);
|
|
@@ -800,16 +855,17 @@
|
|
rendered = suggestions.length;
|
|
rendered = suggestions.length;
|
|
that._overwrite(query, suggestions);
|
|
that._overwrite(query, suggestions);
|
|
if (rendered < that.limit && that.async) {
|
|
if (rendered < that.limit && that.async) {
|
|
- that.trigger("asyncRequested", query);
|
|
|
|
|
|
+ that.trigger("asyncRequested", query, that.name);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
function async(suggestions) {
|
|
function async(suggestions) {
|
|
suggestions = suggestions || [];
|
|
suggestions = suggestions || [];
|
|
if (!canceled && rendered < that.limit) {
|
|
if (!canceled && rendered < that.limit) {
|
|
that.cancel = $.noop;
|
|
that.cancel = $.noop;
|
|
- rendered += suggestions.length;
|
|
|
|
- that._append(query, suggestions.slice(0, that.limit - rendered));
|
|
|
|
- that.async && that.trigger("asyncReceived", query);
|
|
|
|
|
|
+ var idx = Math.abs(rendered - that.limit);
|
|
|
|
+ rendered += idx;
|
|
|
|
+ that._append(query, suggestions.slice(0, idx));
|
|
|
|
+ that.async && that.trigger("asyncReceived", query, that.name);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
},
|
|
@@ -843,7 +899,7 @@
|
|
suggestion: templates.suggestion || suggestionTemplate
|
|
suggestion: templates.suggestion || suggestionTemplate
|
|
};
|
|
};
|
|
function suggestionTemplate(context) {
|
|
function suggestionTemplate(context) {
|
|
- return $("<div>").text(displayFn(context));
|
|
|
|
|
|
+ return $('<div role="option">').attr("id", _.guid()).text(displayFn(context));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
function isValidName(str) {
|
|
function isValidName(str) {
|
|
@@ -884,10 +940,11 @@
|
|
this.trigger.apply(this, arguments);
|
|
this.trigger.apply(this, arguments);
|
|
},
|
|
},
|
|
_allDatasetsEmpty: function allDatasetsEmpty() {
|
|
_allDatasetsEmpty: function allDatasetsEmpty() {
|
|
- return _.every(this.datasets, isDatasetEmpty);
|
|
|
|
- function isDatasetEmpty(dataset) {
|
|
|
|
- return dataset.isEmpty();
|
|
|
|
- }
|
|
|
|
|
|
+ return _.every(this.datasets, _.bind(function isDatasetEmpty(dataset) {
|
|
|
|
+ var isEmpty = dataset.isEmpty();
|
|
|
|
+ this.$node.attr("aria-expanded", !isEmpty);
|
|
|
|
+ return isEmpty;
|
|
|
|
+ }, this));
|
|
},
|
|
},
|
|
_getSelectables: function getSelectables() {
|
|
_getSelectables: function getSelectables() {
|
|
return this.$node.find(this.selectors.selectable);
|
|
return this.$node.find(this.selectors.selectable);
|
|
@@ -912,6 +969,12 @@
|
|
var that = this, onSelectableClick;
|
|
var that = this, onSelectableClick;
|
|
onSelectableClick = _.bind(this._onSelectableClick, this);
|
|
onSelectableClick = _.bind(this._onSelectableClick, this);
|
|
this.$node.on("click.tt", this.selectors.selectable, onSelectableClick);
|
|
this.$node.on("click.tt", this.selectors.selectable, onSelectableClick);
|
|
|
|
+ this.$node.on("mouseover", this.selectors.selectable, function() {
|
|
|
|
+ that.setCursor($(this));
|
|
|
|
+ });
|
|
|
|
+ this.$node.on("mouseleave", function() {
|
|
|
|
+ that._removeCursor();
|
|
|
|
+ });
|
|
_.each(this.datasets, function(dataset) {
|
|
_.each(this.datasets, function(dataset) {
|
|
dataset.onSync("asyncRequested", that._propagate, that).onSync("asyncCanceled", that._propagate, that).onSync("asyncReceived", that._propagate, that).onSync("rendered", that._onRendered, that).onSync("cleared", that._onCleared, that);
|
|
dataset.onSync("asyncRequested", that._propagate, that).onSync("asyncCanceled", that._propagate, that).onSync("asyncReceived", that._propagate, that).onSync("rendered", that._onRendered, that).onSync("cleared", that._onCleared, that);
|
|
});
|
|
});
|
|
@@ -921,9 +984,11 @@
|
|
return this.$node.hasClass(this.classes.open);
|
|
return this.$node.hasClass(this.classes.open);
|
|
},
|
|
},
|
|
open: function open() {
|
|
open: function open() {
|
|
|
|
+ this.$node.scrollTop(0);
|
|
this.$node.addClass(this.classes.open);
|
|
this.$node.addClass(this.classes.open);
|
|
},
|
|
},
|
|
close: function close() {
|
|
close: function close() {
|
|
|
|
+ this.$node.attr("aria-expanded", false);
|
|
this.$node.removeClass(this.classes.open);
|
|
this.$node.removeClass(this.classes.open);
|
|
this._removeCursor();
|
|
this._removeCursor();
|
|
},
|
|
},
|
|
@@ -988,6 +1053,55 @@
|
|
});
|
|
});
|
|
return Menu;
|
|
return Menu;
|
|
}();
|
|
}();
|
|
|
|
+ var Status = function() {
|
|
|
|
+ "use strict";
|
|
|
|
+ function Status(options) {
|
|
|
|
+ this.$el = $("<span></span>", {
|
|
|
|
+ role: "status",
|
|
|
|
+ "aria-live": "polite"
|
|
|
|
+ }).css({
|
|
|
|
+ position: "absolute",
|
|
|
|
+ padding: "0",
|
|
|
|
+ border: "0",
|
|
|
|
+ height: "1px",
|
|
|
|
+ width: "1px",
|
|
|
|
+ "margin-bottom": "-1px",
|
|
|
|
+ "margin-right": "-1px",
|
|
|
|
+ overflow: "hidden",
|
|
|
|
+ clip: "rect(0 0 0 0)",
|
|
|
|
+ "white-space": "nowrap"
|
|
|
|
+ });
|
|
|
|
+ options.$input.after(this.$el);
|
|
|
|
+ _.each(options.menu.datasets, _.bind(function(dataset) {
|
|
|
|
+ if (dataset.onSync) {
|
|
|
|
+ dataset.onSync("rendered", _.bind(this.update, this));
|
|
|
|
+ dataset.onSync("cleared", _.bind(this.cleared, this));
|
|
|
|
+ }
|
|
|
|
+ }, this));
|
|
|
|
+ }
|
|
|
|
+ _.mixin(Status.prototype, {
|
|
|
|
+ update: function update(event, suggestions) {
|
|
|
|
+ var length = suggestions.length;
|
|
|
|
+ var words;
|
|
|
|
+ if (length === 1) {
|
|
|
|
+ words = {
|
|
|
|
+ result: "result",
|
|
|
|
+ is: "is"
|
|
|
|
+ };
|
|
|
|
+ } else {
|
|
|
|
+ words = {
|
|
|
|
+ result: "results",
|
|
|
|
+ is: "are"
|
|
|
|
+ };
|
|
|
|
+ }
|
|
|
|
+ this.$el.text(length + " " + words.result + " " + words.is + " available, use up and down arrow keys to navigate.");
|
|
|
|
+ },
|
|
|
|
+ cleared: function() {
|
|
|
|
+ this.$el.text("");
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ return Status;
|
|
|
|
+ }();
|
|
var DefaultMenu = function() {
|
|
var DefaultMenu = function() {
|
|
"use strict";
|
|
"use strict";
|
|
var s = Menu.prototype;
|
|
var s = Menu.prototype;
|
|
@@ -1052,6 +1166,7 @@
|
|
this.input = o.input;
|
|
this.input = o.input;
|
|
this.menu = o.menu;
|
|
this.menu = o.menu;
|
|
this.enabled = true;
|
|
this.enabled = true;
|
|
|
|
+ this.autoselect = !!o.autoselect;
|
|
this.active = false;
|
|
this.active = false;
|
|
this.input.hasFocus() && this.activate();
|
|
this.input.hasFocus() && this.activate();
|
|
this.dir = this.input.getLangDir();
|
|
this.dir = this.input.getLangDir();
|
|
@@ -1098,8 +1213,12 @@
|
|
_onDatasetCleared: function onDatasetCleared() {
|
|
_onDatasetCleared: function onDatasetCleared() {
|
|
this._updateHint();
|
|
this._updateHint();
|
|
},
|
|
},
|
|
- _onDatasetRendered: function onDatasetRendered(type, dataset, suggestions, async) {
|
|
|
|
|
|
+ _onDatasetRendered: function onDatasetRendered(type, suggestions, async, dataset) {
|
|
this._updateHint();
|
|
this._updateHint();
|
|
|
|
+ if (this.autoselect) {
|
|
|
|
+ var cursorClass = this.selectors.cursor.substr(1);
|
|
|
|
+ this.menu.$node.find(this.selectors.suggestion).first().addClass(cursorClass);
|
|
|
|
+ }
|
|
this.eventBus.trigger("render", suggestions, async, dataset);
|
|
this.eventBus.trigger("render", suggestions, async, dataset);
|
|
},
|
|
},
|
|
_onAsyncRequested: function onAsyncRequested(type, dataset, query) {
|
|
_onAsyncRequested: function onAsyncRequested(type, dataset, query) {
|
|
@@ -1122,7 +1241,15 @@
|
|
_onEnterKeyed: function onEnterKeyed(type, $e) {
|
|
_onEnterKeyed: function onEnterKeyed(type, $e) {
|
|
var $selectable;
|
|
var $selectable;
|
|
if ($selectable = this.menu.getActiveSelectable()) {
|
|
if ($selectable = this.menu.getActiveSelectable()) {
|
|
- this.select($selectable) && $e.preventDefault();
|
|
|
|
|
|
+ if (this.select($selectable)) {
|
|
|
|
+ $e.preventDefault();
|
|
|
|
+ $e.stopPropagation();
|
|
|
|
+ }
|
|
|
|
+ } else if (this.autoselect) {
|
|
|
|
+ if (this.select(this.menu.getTopSelectable())) {
|
|
|
|
+ $e.preventDefault();
|
|
|
|
+ $e.stopPropagation();
|
|
|
|
+ }
|
|
}
|
|
}
|
|
},
|
|
},
|
|
_onTabKeyed: function onTabKeyed(type, $e) {
|
|
_onTabKeyed: function onTabKeyed(type, $e) {
|
|
@@ -1144,12 +1271,12 @@
|
|
},
|
|
},
|
|
_onLeftKeyed: function onLeftKeyed() {
|
|
_onLeftKeyed: function onLeftKeyed() {
|
|
if (this.dir === "rtl" && this.input.isCursorAtEnd()) {
|
|
if (this.dir === "rtl" && this.input.isCursorAtEnd()) {
|
|
- this.autocomplete(this.menu.getTopSelectable());
|
|
|
|
|
|
+ this.autocomplete(this.menu.getActiveSelectable() || this.menu.getTopSelectable());
|
|
}
|
|
}
|
|
},
|
|
},
|
|
_onRightKeyed: function onRightKeyed() {
|
|
_onRightKeyed: function onRightKeyed() {
|
|
if (this.dir === "ltr" && this.input.isCursorAtEnd()) {
|
|
if (this.dir === "ltr" && this.input.isCursorAtEnd()) {
|
|
- this.autocomplete(this.menu.getTopSelectable());
|
|
|
|
|
|
+ this.autocomplete(this.menu.getActiveSelectable() || this.menu.getTopSelectable());
|
|
}
|
|
}
|
|
},
|
|
},
|
|
_onQueryChanged: function onQueryChanged(e, query) {
|
|
_onQueryChanged: function onQueryChanged(e, query) {
|
|
@@ -1249,9 +1376,9 @@
|
|
},
|
|
},
|
|
select: function select($selectable) {
|
|
select: function select($selectable) {
|
|
var data = this.menu.getSelectableData($selectable);
|
|
var data = this.menu.getSelectableData($selectable);
|
|
- if (data && !this.eventBus.before("select", data.obj)) {
|
|
|
|
|
|
+ if (data && !this.eventBus.before("select", data.obj, data.dataset)) {
|
|
this.input.setQuery(data.val, true);
|
|
this.input.setQuery(data.val, true);
|
|
- this.eventBus.trigger("select", data.obj);
|
|
|
|
|
|
+ this.eventBus.trigger("select", data.obj, data.dataset);
|
|
this.close();
|
|
this.close();
|
|
return true;
|
|
return true;
|
|
}
|
|
}
|
|
@@ -1262,21 +1389,24 @@
|
|
query = this.input.getQuery();
|
|
query = this.input.getQuery();
|
|
data = this.menu.getSelectableData($selectable);
|
|
data = this.menu.getSelectableData($selectable);
|
|
isValid = data && query !== data.val;
|
|
isValid = data && query !== data.val;
|
|
- if (isValid && !this.eventBus.before("autocomplete", data.obj)) {
|
|
|
|
|
|
+ if (isValid && !this.eventBus.before("autocomplete", data.obj, data.dataset)) {
|
|
this.input.setQuery(data.val);
|
|
this.input.setQuery(data.val);
|
|
- this.eventBus.trigger("autocomplete", data.obj);
|
|
|
|
|
|
+ this.eventBus.trigger("autocomplete", data.obj, data.dataset);
|
|
return true;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
return false;
|
|
},
|
|
},
|
|
moveCursor: function moveCursor(delta) {
|
|
moveCursor: function moveCursor(delta) {
|
|
- var query, $candidate, data, payload, cancelMove;
|
|
|
|
|
|
+ var query, $candidate, data, suggestion, datasetName, cancelMove, id;
|
|
query = this.input.getQuery();
|
|
query = this.input.getQuery();
|
|
$candidate = this.menu.selectableRelativeToCursor(delta);
|
|
$candidate = this.menu.selectableRelativeToCursor(delta);
|
|
data = this.menu.getSelectableData($candidate);
|
|
data = this.menu.getSelectableData($candidate);
|
|
- payload = data ? data.obj : null;
|
|
|
|
|
|
+ suggestion = data ? data.obj : null;
|
|
|
|
+ datasetName = data ? data.dataset : null;
|
|
|
|
+ id = $candidate ? $candidate.attr("id") : null;
|
|
|
|
+ this.input.trigger("cursorchange", id);
|
|
cancelMove = this._minLengthMet() && this.menu.update(query);
|
|
cancelMove = this._minLengthMet() && this.menu.update(query);
|
|
- if (!cancelMove && !this.eventBus.before("cursorchange", payload)) {
|
|
|
|
|
|
+ if (!cancelMove && !this.eventBus.before("cursorchange", suggestion, datasetName)) {
|
|
this.menu.setCursor($candidate);
|
|
this.menu.setCursor($candidate);
|
|
if (data) {
|
|
if (data) {
|
|
this.input.setInputValue(data.val);
|
|
this.input.setInputValue(data.val);
|
|
@@ -1284,7 +1414,7 @@
|
|
this.input.resetInputValue();
|
|
this.input.resetInputValue();
|
|
this._updateHint();
|
|
this._updateHint();
|
|
}
|
|
}
|
|
- this.eventBus.trigger("cursorchange", payload);
|
|
|
|
|
|
+ this.eventBus.trigger("cursorchange", suggestion, datasetName);
|
|
return true;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
return false;
|
|
@@ -1322,7 +1452,7 @@
|
|
www = WWW(o.classNames);
|
|
www = WWW(o.classNames);
|
|
return this.each(attach);
|
|
return this.each(attach);
|
|
function attach() {
|
|
function attach() {
|
|
- var $input, $wrapper, $hint, $menu, defaultHint, defaultMenu, eventBus, input, menu, typeahead, MenuConstructor;
|
|
|
|
|
|
+ var $input, $wrapper, $hint, $menu, defaultHint, defaultMenu, eventBus, input, menu, status, typeahead, MenuConstructor;
|
|
_.each(datasets, function(d) {
|
|
_.each(datasets, function(d) {
|
|
d.highlight = !!o.highlight;
|
|
d.highlight = !!o.highlight;
|
|
});
|
|
});
|
|
@@ -1353,11 +1483,16 @@
|
|
node: $menu,
|
|
node: $menu,
|
|
datasets: datasets
|
|
datasets: datasets
|
|
}, www);
|
|
}, www);
|
|
|
|
+ status = new Status({
|
|
|
|
+ $input: $input,
|
|
|
|
+ menu: menu
|
|
|
|
+ });
|
|
typeahead = new Typeahead({
|
|
typeahead = new Typeahead({
|
|
input: input,
|
|
input: input,
|
|
menu: menu,
|
|
menu: menu,
|
|
eventBus: eventBus,
|
|
eventBus: eventBus,
|
|
- minLength: o.minLength
|
|
|
|
|
|
+ minLength: o.minLength,
|
|
|
|
+ autoselect: o.autoselect
|
|
}, www);
|
|
}, www);
|
|
$input.data(keys.www, www);
|
|
$input.data(keys.www, www);
|
|
$input.data(keys.typeahead, typeahead);
|
|
$input.data(keys.typeahead, typeahead);
|
|
@@ -1450,7 +1585,7 @@
|
|
return query;
|
|
return query;
|
|
} else {
|
|
} else {
|
|
ttEach(this, function(t) {
|
|
ttEach(this, function(t) {
|
|
- t.setVal(newVal);
|
|
|
|
|
|
+ t.setVal(_.toStr(newVal));
|
|
});
|
|
});
|
|
return this;
|
|
return this;
|
|
}
|
|
}
|
|
@@ -1481,8 +1616,10 @@
|
|
});
|
|
});
|
|
}
|
|
}
|
|
function buildHintFromInput($input, www) {
|
|
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",
|
|
|
|
|
|
+ return $input.clone().addClass(www.classes.hint).removeData().css(www.css.hint).css(getBackgroundStyles($input)).prop({
|
|
|
|
+ readonly: true,
|
|
|
|
+ required: false
|
|
|
|
+ }).removeAttr("id name placeholder").removeClass("required").attr({
|
|
spellcheck: "false",
|
|
spellcheck: "false",
|
|
tabindex: -1
|
|
tabindex: -1
|
|
});
|
|
});
|
|
@@ -1495,7 +1632,6 @@
|
|
style: $input.attr("style")
|
|
style: $input.attr("style")
|
|
});
|
|
});
|
|
$input.addClass(www.classes.input).attr({
|
|
$input.addClass(www.classes.input).attr({
|
|
- autocomplete: "off",
|
|
|
|
spellcheck: false
|
|
spellcheck: false
|
|
});
|
|
});
|
|
try {
|
|
try {
|