|
9 | 9 | define([ "jquery" ], function(a0) { |
10 | 10 | return root["Bloodhound"] = factory(a0); |
11 | 11 | }); |
12 | | - } else if (typeof exports === "object") { |
| 12 | + } else if (typeof module === "object" && module.exports) { |
13 | 13 | module.exports = factory(require("jquery")); |
14 | 14 | } else { |
15 | 15 | root["Bloodhound"] = factory(root["jQuery"]); |
|
148 | 148 | stringify: function(val) { |
149 | 149 | return _.isString(val) ? val : JSON.stringify(val); |
150 | 150 | }, |
| 151 | + guid: function() { |
| 152 | + function _p8(s) { |
| 153 | + var p = (Math.random().toString(16) + "000000000").substr(2, 8); |
| 154 | + return s ? "-" + p.substr(0, 4) + "-" + p.substr(4, 4) : p; |
| 155 | + } |
| 156 | + return "tt-" + _p8() + _p8(true) + _p8(true) + _p8(); |
| 157 | + }, |
151 | 158 | noop: function() {} |
152 | 159 | }; |
153 | 160 | }(); |
|
949 | 956 | define([ "jquery" ], function(a0) { |
950 | 957 | return factory(a0); |
951 | 958 | }); |
952 | | - } else if (typeof exports === "object") { |
| 959 | + } else if (typeof module === "object" && module.exports) { |
953 | 960 | module.exports = factory(require("jquery")); |
954 | 961 | } else { |
955 | 962 | factory(root["jQuery"]); |
|
1088 | 1095 | stringify: function(val) { |
1089 | 1096 | return _.isString(val) ? val : JSON.stringify(val); |
1090 | 1097 | }, |
| 1098 | + guid: function() { |
| 1099 | + function _p8(s) { |
| 1100 | + var p = (Math.random().toString(16) + "000000000").substr(2, 8); |
| 1101 | + return s ? "-" + p.substr(0, 4) + "-" + p.substr(4, 4) : p; |
| 1102 | + } |
| 1103 | + return "tt-" + _p8() + _p8(true) + _p8(true) + _p8(); |
| 1104 | + }, |
1091 | 1105 | noop: function() {} |
1092 | 1106 | }; |
1093 | 1107 | }(); |
|
1129 | 1143 | function buildHtml(c) { |
1130 | 1144 | return { |
1131 | 1145 | wrapper: '<span class="' + c.wrapper + '"></span>', |
1132 | | - menu: '<div class="' + c.menu + '"></div>' |
| 1146 | + menu: '<div role="listbox" class="' + c.menu + '"></div>' |
1133 | 1147 | }; |
1134 | 1148 | } |
1135 | 1149 | function buildSelectors(classes) { |
|
1422 | 1436 | www.mixin(this); |
1423 | 1437 | this.$hint = $(o.hint); |
1424 | 1438 | this.$input = $(o.input); |
| 1439 | + this.$input.attr({ |
| 1440 | + "aria-activedescendant": "", |
| 1441 | + "aria-owns": this.$input.attr("id") + "_listbox", |
| 1442 | + role: "combobox", |
| 1443 | + "aria-readonly": "true", |
| 1444 | + "aria-autocomplete": "list" |
| 1445 | + }); |
| 1446 | + $(www.menu).attr("id", this.$input.attr("id") + "_listbox"); |
1425 | 1447 | this.query = this.$input.val(); |
1426 | 1448 | this.queryWhenFocused = this.hasFocus() ? this.query : null; |
1427 | 1449 | this.$overflowHelper = buildOverflowHelper(this.$input); |
1428 | 1450 | this._checkLanguageDirection(); |
1429 | 1451 | if (this.$hint.length === 0) { |
1430 | 1452 | this.setHint = this.getHint = this.clearHint = this.clearHintIfInvalid = _.noop; |
1431 | 1453 | } |
| 1454 | + this.onSync("cursorchange", this._updateDescendent); |
1432 | 1455 | } |
1433 | 1456 | Input.normalizeQuery = function(str) { |
1434 | 1457 | return _.toStr(str).replace(/^\s*/g, "").replace(/\s{2,}/g, " "); |
|
1498 | 1521 | this.trigger("whitespaceChanged", this.query); |
1499 | 1522 | } |
1500 | 1523 | }, |
| 1524 | + _updateDescendent: function updateDescendent(event, id) { |
| 1525 | + this.$input.attr("aria-activedescendant", id); |
| 1526 | + }, |
1501 | 1527 | bind: function() { |
1502 | 1528 | var that = this, onBlur, onFocus, onKeydown, onInput; |
1503 | 1529 | onBlur = _.bind(this._onBlur, this); |
|
1648 | 1674 | this.source = o.source.__ttAdapter ? o.source.__ttAdapter() : o.source; |
1649 | 1675 | this.async = _.isUndefined(o.async) ? this.source.length > 2 : !!o.async; |
1650 | 1676 | this._resetLastSuggestion(); |
1651 | | - this.$el = $(o.node).addClass(this.classes.dataset).addClass(this.classes.dataset + "-" + this.name); |
| 1677 | + this.$el = $(o.node).attr("role", "presentation").addClass(this.classes.dataset).addClass(this.classes.dataset + "-" + this.name); |
1652 | 1678 | } |
1653 | 1679 | Dataset.extractData = function extractData(el) { |
1654 | 1680 | var $el = $(el); |
|
1820 | 1846 | suggestion: templates.suggestion || suggestionTemplate |
1821 | 1847 | }; |
1822 | 1848 | function suggestionTemplate(context) { |
1823 | | - return $("<div>").text(displayFn(context)); |
| 1849 | + return $('<div role="option">').attr("id", _.guid()).text(displayFn(context)); |
1824 | 1850 | } |
1825 | 1851 | } |
1826 | 1852 | function isValidName(str) { |
|
1861 | 1887 | this.trigger.apply(this, arguments); |
1862 | 1888 | }, |
1863 | 1889 | _allDatasetsEmpty: function allDatasetsEmpty() { |
1864 | | - return _.every(this.datasets, isDatasetEmpty); |
1865 | | - function isDatasetEmpty(dataset) { |
1866 | | - return dataset.isEmpty(); |
1867 | | - } |
| 1890 | + return _.every(this.datasets, _.bind(function isDatasetEmpty(dataset) { |
| 1891 | + var isEmpty = dataset.isEmpty(); |
| 1892 | + this.$node.attr("aria-expanded", !isEmpty); |
| 1893 | + return isEmpty; |
| 1894 | + }, this)); |
1868 | 1895 | }, |
1869 | 1896 | _getSelectables: function getSelectables() { |
1870 | 1897 | return this.$node.find(this.selectors.selectable); |
|
1908 | 1935 | this.$node.addClass(this.classes.open); |
1909 | 1936 | }, |
1910 | 1937 | close: function close() { |
| 1938 | + this.$node.attr("aria-expanded", false); |
1911 | 1939 | this.$node.removeClass(this.classes.open); |
1912 | 1940 | this._removeCursor(); |
1913 | 1941 | }, |
|
1972 | 2000 | }); |
1973 | 2001 | return Menu; |
1974 | 2002 | }(); |
| 2003 | + var Status = function() { |
| 2004 | + "use strict"; |
| 2005 | + function Status(options) { |
| 2006 | + this.el = '<span role="status" aria-live="polite" class="visuallyhidden"></span>'; |
| 2007 | + this.$el = $(this.el); |
| 2008 | + options.$input.after(this.$el); |
| 2009 | + _.each(options.menu.datasets, _.bind(function(dataset) { |
| 2010 | + if (dataset.onSync) { |
| 2011 | + dataset.onSync("rendered", _.bind(this.update, this)); |
| 2012 | + dataset.onSync("cleared", _.bind(this.cleared, this)); |
| 2013 | + } |
| 2014 | + }, this)); |
| 2015 | + } |
| 2016 | + _.mixin(Status.prototype, { |
| 2017 | + update: function update(event, suggestions) { |
| 2018 | + var length = suggestions.length; |
| 2019 | + var words; |
| 2020 | + if (length === 1) { |
| 2021 | + words = { |
| 2022 | + result: "result", |
| 2023 | + is: "is" |
| 2024 | + }; |
| 2025 | + } else { |
| 2026 | + words = { |
| 2027 | + result: "results", |
| 2028 | + is: "are" |
| 2029 | + }; |
| 2030 | + } |
| 2031 | + this.$el.text(length + " " + words.result + " " + words.is + " available, use up and down arrow keys to navigate."); |
| 2032 | + }, |
| 2033 | + cleared: function() { |
| 2034 | + this.$el.text(""); |
| 2035 | + } |
| 2036 | + }); |
| 2037 | + return Status; |
| 2038 | + }(); |
1975 | 2039 | var DefaultMenu = function() { |
1976 | 2040 | "use strict"; |
1977 | 2041 | var s = Menu.prototype; |
|
2257 | 2321 | return false; |
2258 | 2322 | }, |
2259 | 2323 | moveCursor: function moveCursor(delta) { |
2260 | | - var query, $candidate, data, suggestion, datasetName, cancelMove; |
| 2324 | + var query, $candidate, data, suggestion, datasetName, cancelMove, id; |
2261 | 2325 | query = this.input.getQuery(); |
2262 | 2326 | $candidate = this.menu.selectableRelativeToCursor(delta); |
2263 | 2327 | data = this.menu.getSelectableData($candidate); |
2264 | 2328 | suggestion = data ? data.obj : null; |
2265 | 2329 | datasetName = data ? data.dataset : null; |
| 2330 | + id = $candidate ? $candidate.attr("id") : null; |
| 2331 | + this.input.trigger("cursorchange", id); |
2266 | 2332 | cancelMove = this._minLengthMet() && this.menu.update(query); |
2267 | 2333 | if (!cancelMove && !this.eventBus.before("cursorchange", suggestion, datasetName)) { |
2268 | 2334 | this.menu.setCursor($candidate); |
|
2310 | 2376 | www = WWW(o.classNames); |
2311 | 2377 | return this.each(attach); |
2312 | 2378 | function attach() { |
2313 | | - var $input, $wrapper, $hint, $menu, defaultHint, defaultMenu, eventBus, input, menu, typeahead, MenuConstructor; |
| 2379 | + var $input, $wrapper, $hint, $menu, defaultHint, defaultMenu, eventBus, input, menu, status, typeahead, MenuConstructor; |
2314 | 2380 | _.each(datasets, function(d) { |
2315 | 2381 | d.highlight = !!o.highlight; |
2316 | 2382 | }); |
|
2341 | 2407 | node: $menu, |
2342 | 2408 | datasets: datasets |
2343 | 2409 | }, www); |
| 2410 | + status = new Status({ |
| 2411 | + $input: $input, |
| 2412 | + menu: menu |
| 2413 | + }); |
2344 | 2414 | typeahead = new Typeahead({ |
2345 | 2415 | input: input, |
2346 | 2416 | menu: menu, |
|
2470 | 2540 | } |
2471 | 2541 | function buildHintFromInput($input, www) { |
2472 | 2542 | return $input.clone().addClass(www.classes.hint).removeData().css(www.css.hint).css(getBackgroundStyles($input)).prop("readonly", true).removeAttr("id name placeholder required").attr({ |
2473 | | - autocomplete: "off", |
2474 | 2543 | spellcheck: "false", |
2475 | 2544 | tabindex: -1 |
2476 | 2545 | }); |
|
2483 | 2552 | style: $input.attr("style") |
2484 | 2553 | }); |
2485 | 2554 | $input.addClass(www.classes.input).attr({ |
2486 | | - autocomplete: "off", |
2487 | 2555 | spellcheck: false |
2488 | 2556 | }); |
2489 | 2557 | try { |
|
0 commit comments