'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); } }); }); } } }());