Skip to content

Commit 815ae42

Browse files
authored
Merge pull request #104 from colinrotherham/switch-fork
Improve screen reader support, ARIA etc
2 parents 201aa01 + 9c6e693 commit 815ae42

24 files changed

+401
-77
lines changed

Gruntfile.js

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ var semver = require('semver'),
2424
'src/typeahead/input.js',
2525
'src/typeahead/dataset.js',
2626
'src/typeahead/menu.js',
27+
'src/typeahead/status.js',
2728
'src/typeahead/default_menu.js',
2829
'src/typeahead/typeahead.js',
2930
'src/typeahead/plugin.js'
@@ -153,12 +154,25 @@ module.exports = function(grunt) {
153154
}
154155
},
155156

156-
sed: {
157+
replace: {
157158
version: {
158-
pattern: '%VERSION%',
159-
replacement: '<%= version %>',
160-
recursive: true,
161-
path: '<%= buildDir %>'
159+
options: {
160+
patterns: [
161+
{
162+
match: '%VERSION%',
163+
replacement: '<%= version %>',
164+
}
165+
],
166+
usePrefix: false
167+
},
168+
files: [
169+
{
170+
expand: true,
171+
flatten: true,
172+
src: '<%= buildDir %>/**/*.js',
173+
dest: '<%= buildDir %>'
174+
}
175+
]
162176
}
163177
},
164178

@@ -308,14 +322,14 @@ module.exports = function(grunt) {
308322
'uglify:typeaheadMin',
309323
'uglify:bundle',
310324
'uglify:bundleMin',
311-
'sed:version'
325+
'replace:version'
312326
]);
313327

314328
// load tasks
315329
// ----------
316330

317331
grunt.loadNpmTasks('grunt-umd');
318-
grunt.loadNpmTasks('grunt-sed');
332+
grunt.loadNpmTasks('grunt-replace');
319333
grunt.loadNpmTasks('grunt-exec');
320334
grunt.loadNpmTasks('grunt-step');
321335
grunt.loadNpmTasks('grunt-concurrent');

bower.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@
33
"version": "1.0.1",
44
"main": "dist/typeahead.bundle.js",
55
"dependencies": {
6-
"jquery": ">=1.7"
6+
"jquery": ">=1.11"
77
},
88
"devDependencies": {
9-
"jquery": "~1.7",
9+
"jquery": "~1.11",
1010
"jasmine-ajax": "~1.3.1",
1111
"jasmine-jquery": "~1.7.0"
1212
},
1313
"resolutions": {
14-
"jquery": "1.7.2"
14+
"jquery": "1.11.3"
1515
}
1616
}

dist/bloodhound.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
define([ "jquery" ], function(a0) {
1010
return root["Bloodhound"] = factory(a0);
1111
});
12-
} else if (typeof exports === "object") {
12+
} else if (typeof module === "object" && module.exports) {
1313
module.exports = factory(require("jquery"));
1414
} else {
1515
root["Bloodhound"] = factory(root["jQuery"]);
@@ -148,6 +148,13 @@
148148
stringify: function(val) {
149149
return _.isString(val) ? val : JSON.stringify(val);
150150
},
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+
},
151158
noop: function() {}
152159
};
153160
}();

dist/bloodhound.min.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/typeahead.bundle.js

Lines changed: 81 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
define([ "jquery" ], function(a0) {
1010
return root["Bloodhound"] = factory(a0);
1111
});
12-
} else if (typeof exports === "object") {
12+
} else if (typeof module === "object" && module.exports) {
1313
module.exports = factory(require("jquery"));
1414
} else {
1515
root["Bloodhound"] = factory(root["jQuery"]);
@@ -148,6 +148,13 @@
148148
stringify: function(val) {
149149
return _.isString(val) ? val : JSON.stringify(val);
150150
},
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+
},
151158
noop: function() {}
152159
};
153160
}();
@@ -949,7 +956,7 @@
949956
define([ "jquery" ], function(a0) {
950957
return factory(a0);
951958
});
952-
} else if (typeof exports === "object") {
959+
} else if (typeof module === "object" && module.exports) {
953960
module.exports = factory(require("jquery"));
954961
} else {
955962
factory(root["jQuery"]);
@@ -1088,6 +1095,13 @@
10881095
stringify: function(val) {
10891096
return _.isString(val) ? val : JSON.stringify(val);
10901097
},
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+
},
10911105
noop: function() {}
10921106
};
10931107
}();
@@ -1129,7 +1143,7 @@
11291143
function buildHtml(c) {
11301144
return {
11311145
wrapper: '<span class="' + c.wrapper + '"></span>',
1132-
menu: '<div class="' + c.menu + '"></div>'
1146+
menu: '<div role="listbox" class="' + c.menu + '"></div>'
11331147
};
11341148
}
11351149
function buildSelectors(classes) {
@@ -1422,13 +1436,22 @@
14221436
www.mixin(this);
14231437
this.$hint = $(o.hint);
14241438
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");
14251447
this.query = this.$input.val();
14261448
this.queryWhenFocused = this.hasFocus() ? this.query : null;
14271449
this.$overflowHelper = buildOverflowHelper(this.$input);
14281450
this._checkLanguageDirection();
14291451
if (this.$hint.length === 0) {
14301452
this.setHint = this.getHint = this.clearHint = this.clearHintIfInvalid = _.noop;
14311453
}
1454+
this.onSync("cursorchange", this._updateDescendent);
14321455
}
14331456
Input.normalizeQuery = function(str) {
14341457
return _.toStr(str).replace(/^\s*/g, "").replace(/\s{2,}/g, " ");
@@ -1498,6 +1521,9 @@
14981521
this.trigger("whitespaceChanged", this.query);
14991522
}
15001523
},
1524+
_updateDescendent: function updateDescendent(event, id) {
1525+
this.$input.attr("aria-activedescendant", id);
1526+
},
15011527
bind: function() {
15021528
var that = this, onBlur, onFocus, onKeydown, onInput;
15031529
onBlur = _.bind(this._onBlur, this);
@@ -1648,7 +1674,7 @@
16481674
this.source = o.source.__ttAdapter ? o.source.__ttAdapter() : o.source;
16491675
this.async = _.isUndefined(o.async) ? this.source.length > 2 : !!o.async;
16501676
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);
16521678
}
16531679
Dataset.extractData = function extractData(el) {
16541680
var $el = $(el);
@@ -1820,7 +1846,7 @@
18201846
suggestion: templates.suggestion || suggestionTemplate
18211847
};
18221848
function suggestionTemplate(context) {
1823-
return $("<div>").text(displayFn(context));
1849+
return $('<div role="option">').attr("id", _.guid()).text(displayFn(context));
18241850
}
18251851
}
18261852
function isValidName(str) {
@@ -1861,10 +1887,11 @@
18611887
this.trigger.apply(this, arguments);
18621888
},
18631889
_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));
18681895
},
18691896
_getSelectables: function getSelectables() {
18701897
return this.$node.find(this.selectors.selectable);
@@ -1908,6 +1935,7 @@
19081935
this.$node.addClass(this.classes.open);
19091936
},
19101937
close: function close() {
1938+
this.$node.attr("aria-expanded", false);
19111939
this.$node.removeClass(this.classes.open);
19121940
this._removeCursor();
19131941
},
@@ -1972,6 +2000,42 @@
19722000
});
19732001
return Menu;
19742002
}();
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+
}();
19752039
var DefaultMenu = function() {
19762040
"use strict";
19772041
var s = Menu.prototype;
@@ -2257,12 +2321,14 @@
22572321
return false;
22582322
},
22592323
moveCursor: function moveCursor(delta) {
2260-
var query, $candidate, data, suggestion, datasetName, cancelMove;
2324+
var query, $candidate, data, suggestion, datasetName, cancelMove, id;
22612325
query = this.input.getQuery();
22622326
$candidate = this.menu.selectableRelativeToCursor(delta);
22632327
data = this.menu.getSelectableData($candidate);
22642328
suggestion = data ? data.obj : null;
22652329
datasetName = data ? data.dataset : null;
2330+
id = $candidate ? $candidate.attr("id") : null;
2331+
this.input.trigger("cursorchange", id);
22662332
cancelMove = this._minLengthMet() && this.menu.update(query);
22672333
if (!cancelMove && !this.eventBus.before("cursorchange", suggestion, datasetName)) {
22682334
this.menu.setCursor($candidate);
@@ -2310,7 +2376,7 @@
23102376
www = WWW(o.classNames);
23112377
return this.each(attach);
23122378
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;
23142380
_.each(datasets, function(d) {
23152381
d.highlight = !!o.highlight;
23162382
});
@@ -2341,6 +2407,10 @@
23412407
node: $menu,
23422408
datasets: datasets
23432409
}, www);
2410+
status = new Status({
2411+
$input: $input,
2412+
menu: menu
2413+
});
23442414
typeahead = new Typeahead({
23452415
input: input,
23462416
menu: menu,
@@ -2470,7 +2540,6 @@
24702540
}
24712541
function buildHintFromInput($input, www) {
24722542
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",
24742543
spellcheck: "false",
24752544
tabindex: -1
24762545
});
@@ -2483,7 +2552,6 @@
24832552
style: $input.attr("style")
24842553
});
24852554
$input.addClass(www.classes.input).attr({
2486-
autocomplete: "off",
24872555
spellcheck: false
24882556
});
24892557
try {

dist/typeahead.bundle.min.js

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)