'use strict';
if (typeof (BSC ) === 'undefined') {
var BSC = {};
}
BSC.typeahead = ( function () {
return {
init: function () {
$('.typeahead').each(function () {
var that = $(this);
var isDropdown = that.attr('data-typeahead-dropdown') != undefined;
var limit = parseInt(that.attr('data-typeahead-dropdown'), 10) || 100;
var source = that.attr('data-source');
$.get(source, function (data) {
var configList = data.map(function(data) {
var engine = new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.whitespace,
queryTokenizer: Bloodhound.tokenizers.whitespace,
local: data.data
});
var preprocessing = function (q, sync) {
if (q === '') {
sync(engine.index.all());
} else {
engine.search(q, sync);
}
};
var header = null;
if (data.label) {
header = function() {
var el = $('
');
el.find('span').text(data.label);
return el;
};
}
return {
name: data.label || 'name',
source: isDropdown ? preprocessing : engine,
limit: limit,
templates: {
header: header,
notFound: 'Sorry, we couldn\'t find any results.
',
suggestion: function(value) {
var el =$('');
el.find('div').text(value);
return el;
}
}
};
});
var config = {
hint: true,
highlight: true,
minLength: isDropdown ? 0 : 1,
showDefault: true
};
// initialize typeahead
that.typeahead.apply(that, [config].concat(configList));
var elWrapper = that.closest('.twitter-typeahead'),
elMenu = elWrapper.find('.tt-menu'),
elInput = elWrapper.find('.tt-input');
// remove background image that's auto-inserted by typeahead library on IE
elInput.css('background-image', '');
function isMenuOpen() {
var val = that.typeahead('val'),
normalizedVal = that.data('tt-typeahead').input.constructor.normalizeQuery(val);
// from testing the typeahead implementation, the dropdown is open when any
// of the following conditions are met:
return !!normalizedVal // left-trimmed value is non-empty
|| isDropdown // it is a dropdown list
|| elWrapper.find('.tt-menu.tt-open .not-found').length > 0; // not found message is visible
}
that.on('typeahead:render', function(e) {
// add class to wrapper to allow for styling when no suggestions availble
elWrapper.toggleClass('is-not-found', !elMenu.find('.tt-suggestion').length);
});
// event triggered on each input field change
that.data('tt-typeahead').input.onSync('queryChanged', function(e) {
elWrapper.toggleClass('is-menu-open', isMenuOpen());
});
// ISSUE: On iOS, when keyboard is active, fixed positioned elements become absolutely positioned
// WORKAROUND: Scroll user to the top of the page in order to see dropdown menu, but remember
// original page position. Once the dropdown menu is closed, move the scroll position back to where
// it was originally (before the menu was opened).
var originalScrollTop = null; // keeps track of original position
var isFullScreenActive = false; // keeps track of whether menu has been opened in full screen or not
function updateFullScreenPosition() {
// check if we are opening drop down menu in full screen or not
var isFullScreen = elWrapper.css('position') == 'fixed';
if (isFullScreen) {
// always scroll to the top of the window in full screen
setTimeout(function() {
$(window).scrollTop(0);
}, 400);
// check if screen already opened
if (isFullScreenActive) return;
// record current scroll position of page
originalScrollTop = {
html: $('html').scrollTop(),
body: $('body').scrollTop()
};
// disable page from scrolling
// this ensures we will be at the top of the page
$('html, body').css({
height: '100%',
overflow: 'hidden'
});
} else {
if (!isFullScreenActive) return;
// re-enable page scrolling
$('html, body').css({
height: '',
overflow: ''
});
// scroll back to original position
if (originalScrollTop) {
$('html').scrollTop(originalScrollTop.html);
$('body').scrollTop(originalScrollTop.body);
originalScrollTop = null;
}
}
// update full screen active state
isFullScreenActive = isFullScreen;
}
$(window).on('resize', function() {
// updated full screen positioning on window resize
updateFullScreenPosition();
});
that.on('typeahead:open typeahead:close', function() {
// updated full screen positioning on dropdown menu open/close
updateFullScreenPosition();
});
// add class to wrapper depending on whether menu is open or not
that
.on('typeahead:open', function(e) {
elWrapper.toggleClass('is-menu-open', isMenuOpen());
})
.on('typeahead:close', function(e) {
elWrapper.removeClass('is-menu-open');
});
// add control icon and tie its behaviour to typeahead events
elInput.each(function () {
var that = $(this);
that.after('');
elMenu.bind('cssClassAdded', function (e, css) {
if (css === 'tt-open') {
elWrapper.addClass('is-open');
}
});
elMenu.bind('cssClassRemoved', function (e, css) {
if (css === 'tt-open') {
elWrapper.removeClass('is-open');
}
});
// dropdown control for clicking and closing
var ignoreControlClick = false;
var control = elWrapper.find('.tt-control');
control.on('mousedown', function (e) {
ignoreControlClick = elMenu.hasClass('tt-open');
}).on('mouseup', function () {
if (!ignoreControlClick) {
that.focus();
}
ignoreControlClick = false;
});
// dropdown typeahead needs data-typeahead-dropdown attribute
if (that.attr('data-typeahead-dropdown') != undefined) {
elWrapper.addClass('is-dropdown');
}
});
// add placeholder field instruction at the top of the opened dropdown
if (isDropdown) {
var elInstructions = $('').text(elInput.attr('placeholder'));
elMenu.prepend(elInstructions);
}
});
});
}
}
}());