Bläddra i källkod

Update typeahead.js lib

Daniel Supernault 7 år sedan
förälder
incheckning
95fdf03d36
1 ändrade filer med 182 tillägg och 46 borttagningar
  1. 182 46
      resources/assets/js/lib/typeahead.js

+ 182 - 46
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: '<span class="' + c.wrapper + '"></span>',
-                menu: '<div class="' + c.menu + '"></div>'
+                menu: '<div role="listbox" class="' + c.menu + '"></div>'
             };
         }
         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 $("<div>").text(displayFn(context));
+                return $('<div role="option">').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 = $("<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() {
         "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 {