in public/components/directives/ui-filter-list.js [12:147]
link: function(scope, element, attrs) {
var collectionName = attrs.uiFilterList;
function clearResults() {
$q.when(scope[collectionName] || []).then(function (collection) {
collection.length = 0;
});
scope.$apply();
}
// Select the first item when the list changes
scope.$watch(collectionName, function (collection) {
if (collection && collection.length > 0) {
collection[0].selected = true;
}
});
/**
* Reset the list when clicking out of the input
*/
// Note: we used to observe the blur event on the
// element, but that is triggered on mousedown, so if
// you click slowly, the field is blurred before you
// mouseup on your selection. As a workaround, wait
// until after mouseup to clear the list.
// A nicer solution would be:
// http://stackoverflow.com/a/12627314/1082754
// but that requires changing this directive to own
// the list DOM as well.
function mouseUpListener() {
// Blurring when the list is not empty
var isFocused = element[0] === document.activeElement;
if (! isFocused && scope[collectionName]) {
// Extra superstition/safety to let click
// handlers capture events on the list first.
setTimeout(function () {
clearResults();
}, 100);
}
}
var mouseUpBound = false;
scope.$watch(collectionName, function (collection) {
var empty = ! collection || collection.length === 0;
if (empty && mouseUpBound) {
win.unbind('mouseup', mouseUpListener);
mouseUpBound = false;
} else if (! empty && ! mouseUpBound) {
win.bind('mouseup', mouseUpListener);
mouseUpBound = true;
}
});
element.bind('input', function (_event) {
if (attrs.uiFilterListOnFilter) {
scope.$apply(attrs.uiFilterListOnFilter);
}
});
element.bind('keydown', function (event) {
if (event.keyCode === getKeyCodes('enter')) {
event.preventDefault();
// Allows for accepting a value as a promise or just a primitive
// TODO: if we used an isolate scope, this would be resolved for us… but then
// our templates would be bloated with references to `$parent`
$q.when(scope[collectionName] || []).then(function (collection) {
var selected = _.find(collection, function (model) {
return model.selected;
});
if (selected) {
scope.selectedItem = selected.item;
scope.$eval(attrs.uiFilterListOnSelect);
}
});
}
// Browse the list with up/down keys
// Allows for accepting a value as a promise or just a primitive
// TODO: if we used an isolate scope, this would be resolved for us… but then
// our templates would be bloated with references to `$parent`
$q.when(scope[collectionName] || []).then(function (collection) {
// Map key codes to an offset which will determine what item
// is selected next in the collection
var map = {};
map[getKeyCodes('up')] = -1;
map[getKeyCodes('down')] = 1;
var offset = map[event.keyCode];
if (!offset) { return; }
// If the up or down keys were pressed, only then can we
// prevent default (the input by default moves the caret).
// This is done before verifying the collection to ensure
// a consistent user experience.
event.preventDefault();
var selected = _.find(collection, function (model) {
return model.selected;
});
// Deslect the currently selected item
if (selected) {
selected.selected = false;
}
var selectedIndex = collection.indexOf(selected);
// Ensure a default index if there is not one
if (selectedIndex === -1) {
selectedIndex = 0;
}
// Find the item to select, and select it
var length = collection.length;
var futureIndex = ((selectedIndex + offset) + length) % length;
var futureItem = collection[futureIndex];
if (futureItem) {
futureItem.selected = true;
}
});
scope.$apply();
});
// Escape key
element.bind('keydown', function (event) {
if (event.keyCode === getKeyCodes('escape')) {
clearResults();
}
});
}