szurubooru/public_html/js/Controls/AutoCompleteInput.js

242 lines
5.9 KiB
JavaScript
Raw Normal View History

2014-10-05 13:25:40 +02:00
var App = App || {};
App.Controls = App.Controls || {};
App.Controls.AutoCompleteInput = function($input) {
var _ = App.DI.get('_');
var jQuery = App.DI.get('jQuery');
var tagList = App.DI.get('tagList');
var KEY_RETURN = 13;
var KEY_ESCAPE = 27;
var KEY_UP = 38;
var KEY_DOWN = 40;
var options = {
caseSensitive: false,
source: null,
maxResults: 15,
minLengthToArbitrarySearch: 3,
onApply: null,
additionalFilter: null,
};
var showTimeout = null;
var cachedSource = null;
var results = [];
var activeResult = -1;
if ($input.length === 0) {
throw new Error('Input element was not found');
}
if ($input.length > 1) {
throw new Error('Cannot add autocompletion to more than one element at once');
}
if ($input.attr('data-autocomplete')) {
throw new Error('Autocompletion was already added for this element');
}
$input.attr('data-autocomplete', true);
$input.attr('autocomplete', 'off');
var $div = jQuery('<div>');
var $list = jQuery('<ul>');
$div.addClass('autocomplete');
$div.append($list);
jQuery(document.body).append($div);
function getSource() {
if (cachedSource) {
return cachedSource;
} else {
2014-10-14 22:40:34 +02:00
var source = tagList.getTags();
source = _.sortBy(source, function(a) { return -a.usages; });
source = _.filter(source, function(a) { return a.usages > 0; });
source = _.map(source, function(a) {
return {
tag: a.name,
caption: a.name + ' (' + a.usages + ')',
};
});
2014-10-05 13:25:40 +02:00
cachedSource = source;
return source;
}
}
$input.bind('keydown', function(e) {
if (isShown() && e.which === KEY_ESCAPE) {
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
hide();
} else if (isShown() && e.which === KEY_DOWN) {
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
selectNext();
} else if (isShown() && e.which === KEY_UP) {
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
selectPrevious();
} else if (isShown() && e.which === KEY_RETURN && activeResult >= 0) {
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
applyAutocomplete();
hide();
} else {
window.clearTimeout(showTimeout);
showTimeout = window.setTimeout(showOrHide, 250);
}
});
$input.blur(function(e) {
window.clearTimeout(showTimeout);
window.setTimeout(function() { hide(); }, 50);
});
function getSelectionStart(){
var input = $input.get(0);
if (!input) {
return;
}
if ('selectionStart' in input) {
return input.selectionStart;
} else if (document.selection) {
input.focus();
var sel = document.selection.createRange();
var selLen = document.selection.createRange().text.length;
sel.moveStart('character', -input.value.length);
return sel.text.length - selLen;
} else {
return 0;
}
}
function getTextToFind() {
var val = $input.val();
var start = getSelectionStart();
return val.substring(0, start).replace(/.*\s/, '');
}
function showOrHide() {
var textToFind = getTextToFind();
if (textToFind.length === 0) {
hide();
} else {
updateResults(textToFind);
refreshList();
}
}
function isShown() {
return $div.is(':visible');
}
function hide() {
$div.hide();
}
function selectPrevious() {
select(activeResult === -1 ? results.length - 1 : activeResult - 1);
}
function selectNext() {
select(activeResult === -1 ? 0 : activeResult + 1);
}
function select(newActiveResult) {
if (newActiveResult >= 0 && newActiveResult < results.length) {
activeResult = newActiveResult;
refreshActiveResult();
} else {
activeResult = - 1;
refreshActiveResult();
}
}
function getResultsFilter(textToFind) {
if (textToFind.length < options.minLengthToArbitrarySearch) {
return options.caseSensitive ?
2014-10-14 22:40:34 +02:00
function(resultItem) { return resultItem.tag.indexOf(textToFind) === 0; } :
function(resultItem) { return resultItem.tag.toLowerCase().indexOf(textToFind.toLowerCase()) === 0; };
2014-10-05 13:25:40 +02:00
} else {
return options.caseSensitive ?
2014-10-14 22:40:34 +02:00
function(resultItem) { return resultItem.tag.indexOf(textToFind) >= 0; } :
function(resultItem) { return resultItem.tag.toLowerCase().indexOf(textToFind.toLowerCase()) >= 0; };
2014-10-05 13:25:40 +02:00
}
}
function updateResults(textToFind) {
var oldResults = results.slice();
2014-10-05 13:25:40 +02:00
var source = getSource();
var filter = getResultsFilter(textToFind);
results = _.filter(source, filter);
if (options.additionalFilter) {
results = options.additionalFilter(results);
}
results = results.slice(0, options.maxResults);
if (!_.isEqual(oldResults, results)) {
activeResult = -1;
}
2014-10-05 13:25:40 +02:00
}
function applyAutocomplete() {
if (options.onApply) {
2014-10-14 22:40:34 +02:00
options.onApply(results[activeResult].tag);
2014-10-05 13:25:40 +02:00
} else {
var val = $input.val();
var start = getSelectionStart();
var prefix = '';
var suffix = val.substring(start);
var middle = val.substring(0, start);
var index = middle.lastIndexOf(' ');
if (index !== -1) {
prefix = val.substring(0, index + 1);
middle = val.substring(index + 1);
}
2014-10-14 22:40:34 +02:00
$input.val(prefix + results[activeResult].tag + ' ' + suffix.trimLeft());
2014-10-05 13:25:40 +02:00
$input.focus();
}
}
function refreshList() {
if (results.length === 0) {
hide();
return;
}
$list.empty();
_.each(results, function(resultItem, resultIndex) {
2014-10-05 13:25:40 +02:00
var $listItem = jQuery('<li/>');
2014-10-14 22:40:34 +02:00
$listItem.text(resultItem.caption);
$listItem.attr('data-key', resultItem.tag);
$listItem.hover(function(e) {
e.preventDefault();
activeResult = resultIndex;
refreshActiveResult();
});
$listItem.mousedown(function(e) {
e.preventDefault();
activeResult = resultIndex;
applyAutocomplete();
hide();
});
2014-10-05 13:25:40 +02:00
$list.append($listItem);
});
refreshActiveResult();
$div.css({
left: ($input.offset().left) + 'px',
top: ($input.offset().top + $input.outerHeight() - 2) + 'px',
});
$div.show();
}
function refreshActiveResult() {
$list.find('li.active').removeClass('active');
if (activeResult >= 0) {
$list.find('li').eq(activeResult).addClass('active');
}
}
return options;
};