diff --git a/resources/assets/js/lib/typeahead.js b/resources/assets/js/lib/typeahead.js old mode 100644 new mode 100755 index 2b0892897..f80bb192b --- a/resources/assets/js/lib/typeahead.js +++ b/resources/assets/js/lib/typeahead.js @@ -1,18 +1,18 @@ /*! - * typeahead.js 0.11.1 + * typeahead.js 1.2.0 * 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) { if (typeof define === "function" && define.amd) { - define("typeahead.js", [ "jquery" ], function(a0) { + define([ "jquery" ], function(a0) { return factory(a0); }); } else if (typeof exports === "object") { module.exports = factory(require("jquery")); } else { - factory(jQuery); + factory(root["jQuery"]); } })(this, function($) { var _ = function() { @@ -148,6 +148,13 @@ stringify: function(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() {} }; }(); @@ -189,7 +196,7 @@ function buildHtml(c) { return { wrapper: '', - menu: '
' + menu: '
' }; } function buildSelectors(classes) { @@ -264,10 +271,8 @@ } _.mixin(EventBus.prototype, { _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; }, before: function(type) { @@ -384,7 +389,36 @@ tagName: "strong", className: null, 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) { var regex; @@ -393,7 +427,7 @@ return; } 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); function hightlightTextNode(textNode) { 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; 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("|") + ")"; return caseSensitive ? new RegExp(regexStr) : new RegExp(regexStr, "i"); @@ -448,6 +489,14 @@ www.mixin(this); this.$hint = $(o.hint); 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.queryWhenFocused = this.hasFocus() ? this.query : null; this.$overflowHelper = buildOverflowHelper(this.$input); @@ -455,6 +504,7 @@ if (this.$hint.length === 0) { this.setHint = this.getHint = this.clearHint = this.clearHintIfInvalid = _.noop; } + this.onSync("cursorchange", this._updateDescendent); } Input.normalizeQuery = function(str) { return _.toStr(str).replace(/^\s*/g, "").replace(/\s{2,}/g, " "); @@ -524,6 +574,9 @@ this.trigger("whitespaceChanged", this.query); } }, + _updateDescendent: function updateDescendent(event, id) { + this.$input.attr("aria-activedescendant", id); + }, bind: function() { var that = this, onBlur, onFocus, onKeydown, onInput; onBlur = _.bind(this._onBlur, this); @@ -647,6 +700,7 @@ "use strict"; var keys, nameGenerator; keys = { + dataset: "tt-selectable-dataset", val: "tt-selectable-display", obj: "tt-selectable-object" }; @@ -666,19 +720,20 @@ } www.mixin(this); this.highlight = !!o.highlight; - this.name = o.name || nameGenerator(); + this.name = _.toStr(o.name || nameGenerator()); this.limit = o.limit || 5; this.displayFn = getDisplayFn(o.display || o.displayKey); this.templates = getTemplates(o.templates, this.displayFn); this.source = o.source.__ttAdapter ? o.source.__ttAdapter() : o.source; this.async = _.isUndefined(o.async) ? this.source.length > 2 : !!o.async; 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) { var $el = $(el); if ($el.data(keys.obj)) { return { + dataset: $el.data(keys.dataset) || "", val: $el.data(keys.val) || "", obj: $el.data(keys.obj) || null }; @@ -697,7 +752,7 @@ } else { this._empty(); } - this.trigger("rendered", this.name, suggestions, false); + this.trigger("rendered", suggestions, false, this.name); }, _append: function append(query, suggestions) { suggestions = suggestions || []; @@ -708,7 +763,7 @@ } else if (!this.$lastSuggestion.length && this.templates.notFound) { this._renderNotFound(query); } - this.trigger("rendered", this.name, suggestions, true); + this.trigger("rendered", suggestions, true, this.name); }, _renderSuggestions: function renderSuggestions(query, suggestions) { var $fragment; @@ -749,7 +804,7 @@ _.each(suggestions, function getSuggestionNode(suggestion) { var $el, context; 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]); }); this.highlight && highlight({ @@ -787,7 +842,7 @@ this.cancel = function cancel() { canceled = true; that.cancel = $.noop; - that.async && that.trigger("asyncCanceled", query); + that.async && that.trigger("asyncCanceled", query, that.name); }; this.source(query, sync, async); !syncCalled && sync([]); @@ -800,16 +855,17 @@ rendered = suggestions.length; that._overwrite(query, suggestions); if (rendered < that.limit && that.async) { - that.trigger("asyncRequested", query); + that.trigger("asyncRequested", query, that.name); } } function async(suggestions) { suggestions = suggestions || []; if (!canceled && rendered < that.limit) { 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 }; function suggestionTemplate(context) { - return $("
").text(displayFn(context)); + return $('
').attr("id", _.guid()).text(displayFn(context)); } } function isValidName(str) { @@ -884,10 +940,11 @@ this.trigger.apply(this, arguments); }, _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() { return this.$node.find(this.selectors.selectable); @@ -912,6 +969,12 @@ var that = this, onSelectableClick; onSelectableClick = _.bind(this._onSelectableClick, this); 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) { 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); }, open: function open() { + this.$node.scrollTop(0); this.$node.addClass(this.classes.open); }, close: function close() { + this.$node.attr("aria-expanded", false); this.$node.removeClass(this.classes.open); this._removeCursor(); }, @@ -988,6 +1053,55 @@ }); return Menu; }(); + var Status = function() { + "use strict"; + function Status(options) { + this.$el = $("", { + 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() { "use strict"; var s = Menu.prototype; @@ -1052,6 +1166,7 @@ this.input = o.input; this.menu = o.menu; this.enabled = true; + this.autoselect = !!o.autoselect; this.active = false; this.input.hasFocus() && this.activate(); this.dir = this.input.getLangDir(); @@ -1098,8 +1213,12 @@ _onDatasetCleared: function onDatasetCleared() { this._updateHint(); }, - _onDatasetRendered: function onDatasetRendered(type, dataset, suggestions, async) { + _onDatasetRendered: function onDatasetRendered(type, suggestions, async, dataset) { 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); }, _onAsyncRequested: function onAsyncRequested(type, dataset, query) { @@ -1122,7 +1241,15 @@ _onEnterKeyed: function onEnterKeyed(type, $e) { var $selectable; 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) { @@ -1144,12 +1271,12 @@ }, _onLeftKeyed: function onLeftKeyed() { if (this.dir === "rtl" && this.input.isCursorAtEnd()) { - this.autocomplete(this.menu.getTopSelectable()); + this.autocomplete(this.menu.getActiveSelectable() || this.menu.getTopSelectable()); } }, _onRightKeyed: function onRightKeyed() { 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) { @@ -1249,9 +1376,9 @@ }, select: function select($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.eventBus.trigger("select", data.obj); + this.eventBus.trigger("select", data.obj, data.dataset); this.close(); return true; } @@ -1262,21 +1389,24 @@ query = this.input.getQuery(); data = this.menu.getSelectableData($selectable); 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.eventBus.trigger("autocomplete", data.obj); + this.eventBus.trigger("autocomplete", data.obj, data.dataset); return true; } return false; }, moveCursor: function moveCursor(delta) { - var query, $candidate, data, payload, cancelMove; + var query, $candidate, data, suggestion, datasetName, cancelMove, id; query = this.input.getQuery(); $candidate = this.menu.selectableRelativeToCursor(delta); 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); - if (!cancelMove && !this.eventBus.before("cursorchange", payload)) { + if (!cancelMove && !this.eventBus.before("cursorchange", suggestion, datasetName)) { this.menu.setCursor($candidate); if (data) { this.input.setInputValue(data.val); @@ -1284,7 +1414,7 @@ this.input.resetInputValue(); this._updateHint(); } - this.eventBus.trigger("cursorchange", payload); + this.eventBus.trigger("cursorchange", suggestion, datasetName); return true; } return false; @@ -1322,7 +1452,7 @@ www = WWW(o.classNames); return this.each(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) { d.highlight = !!o.highlight; }); @@ -1353,11 +1483,16 @@ node: $menu, datasets: datasets }, www); + status = new Status({ + $input: $input, + menu: menu + }); typeahead = new Typeahead({ input: input, menu: menu, eventBus: eventBus, - minLength: o.minLength + minLength: o.minLength, + autoselect: o.autoselect }, www); $input.data(keys.www, www); $input.data(keys.typeahead, typeahead); @@ -1450,7 +1585,7 @@ return query; } else { ttEach(this, function(t) { - t.setVal(newVal); + t.setVal(_.toStr(newVal)); }); return this; } @@ -1481,8 +1616,10 @@ }); } 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", tabindex: -1 }); @@ -1495,7 +1632,6 @@ style: $input.attr("style") }); $input.addClass(www.classes.input).attr({ - autocomplete: "off", spellcheck: false }); try {