// ## Tag Selector UI
/*global window, document, $ */
(function () {
"use strict";
var suggestions,
categoryOffset,
existingTags = [], // This will be replaced by an API return.
keys = {
UP: 38,
DOWN: 40,
ESC: 27,
ENTER: 13,
COMMA: 188,
BACKSPACE: 8
};
function findTerms(searchTerm, array) {
searchTerm = searchTerm.toUpperCase();
return $.map(array, function (item) {
var match = item.toUpperCase().indexOf(searchTerm) !== -1;
return match ? item : null;
});
}
function showSuggestions($target, searchTerm) {
suggestions.show();
var results = findTerms(searchTerm, existingTags),
pos = $target.position(),
styles = {
left: pos.left
},
maxSuggestions = 5, // Limit the suggestions number
results_length = results.length,
i,
suggest;
suggestions.css(styles);
suggestions.html("");
if (results_length < maxSuggestions) {
maxSuggestions = results_length;
}
for (i = 0; i < maxSuggestions; i += 1) {
suggestions.append("
" + results[i] + "");
}
suggest = $('ul.suggestions li a:contains("' + searchTerm + '")');
suggest.each(function () {
var src_str = $(this).html(),
term = searchTerm,
pattern;
term = term.replace(/(\s+)/, "(<[^>]+>)*$1(<[^>]+>)*");
pattern = new RegExp("(" + term + ")", "i");
src_str = src_str.replace(pattern, "$1");
/*jslint regexp: true */ // - would like to remove this
src_str = src_str.replace(/([^<>]*)((<[^>]+>)+)([^<>]*<\/mark>)/, "$1$2$4");
$(this).html(src_str);
});
}
function handleTagKeyup(e) {
var $target = $(e.currentTarget),
searchTerm = $.trim($target.val()).toLowerCase(),
category,
populator;
if (e.keyCode === keys.UP) {
e.preventDefault();
if (suggestions.is(":visible")) {
if (suggestions.children(".selected").length === 0) {
suggestions.find("li:last-child").addClass('selected');
} else {
suggestions.children(".selected").removeClass('selected').prev().addClass('selected');
}
}
} else if (e.keyCode === keys.DOWN) {
e.preventDefault();
if (suggestions.is(":visible")) {
if (suggestions.children(".selected").length === 0) {
suggestions.find("li:first-child").addClass('selected');
} else {
suggestions.children(".selected").removeClass('selected').next().addClass('selected');
}
}
} else if (e.keyCode === keys.ESC) {
suggestions.hide();
} else if ((e.keyCode === keys.ENTER || e.keyCode === keys.COMMA) && searchTerm) {
// Submit tag using enter or comma key
e.preventDefault();
if (suggestions.is(":visible") && suggestions.children(".selected").length !== 0) {
if ($('.category:containsExact("' + suggestions.children(".selected").text() + '")').length === 0) {
category = $('' + suggestions.children(".selected").text() + '');
if ($target.data('populate')) {
populator = $($target.data('populate'));
populator.append(category);
}
}
suggestions.hide();
} else {
if (e.keyCode === keys.COMMA) {
searchTerm = searchTerm.replace(",", "");
} // Remove comma from string if comma is uses to submit.
if ($('.category:containsExact("' + searchTerm + '")').length === 0) {
category = $('' + searchTerm + '');
if ($target.data('populate')) {
populator = $($target.data('populate'));
populator.append(category);
}
}
}
$target.val('').focus();
searchTerm = ""; // Used to reset search term
suggestions.hide();
}
if (e.keyCode === keys.UP || e.keyCode === keys.DOWN) {
return false;
}
if (searchTerm) {
showSuggestions($target, searchTerm);
} else {
suggestions.hide();
}
}
function handleTagKeyDown(e) {
var $target = $(e.currentTarget),
populator,
lastBlock;
// Delete character tiggers on Keydown, so needed to check on that event rather than Keyup.
if (e.keyCode === keys.BACKSPACE && !$target.val()) {
populator = $($target.data('populate'));
lastBlock = populator.find('.category').last();
lastBlock.remove();
}
}
function handleSuggestionClick(e) {
var $target = $(e.currentTarget),
category = $('' + $(e.currentTarget).text() + ''),
populator;
if ($target.parent().data('populate')) {
populator = $($target.parent().data('populate'));
populator.append(category);
suggestions.hide();
$('[data-input-behaviour="tag"]').val('').focus();
}
}
function handleCategoryClick(e) {
$(e.currentTarget).remove();
}
function handleClickOff(e) {
if (window.matchMedia('(max-width: 650px)').matches) {
e.preventDefault();
$('body').toggleClass('off-canvas');
}
}
$(document).ready(function () {
suggestions = $("ul.suggestions").hide(); // Initnialise suggestions overlay
if ($('.category-input').length) {
categoryOffset = $('.category-input').offset().left;
$('.category-blocks').css({'left': categoryOffset + 'px'});
}
$('[data-input-behaviour="tag"]')
.on('keyup', handleTagKeyup)
.on('keydown', handleTagKeyDown);
$('ul.suggestions').on('click', "li", handleSuggestionClick);
$('.categories').on('click', ".category", handleCategoryClick);
$('[data-off-canvas]').on('click', handleClickOff);
});
}());