; (function ($, window, document, undefined){ "use strict"; window = (typeof window != 'undefined' && window.Math == Math)? window: (typeof self != 'undefined' && self.Math == Math)? self: Function('return this')(); $.fn.dropdown = function (parameters){ var $allModules = $(this), $document = $(document), moduleSelector = $allModules.selector || '', hasTouch = ('ontouchstart' in document.documentElement), time = new Date().getTime(), performance = [] , query = arguments[0], methodInvoked = (typeof query == 'string'), queryArguments = [] .slice.call(arguments, 1), returnedValue; $allModules.each(function (elementIndex){ var settings = ($.isPlainObject(parameters))? $.extend(true , { } , $.fn.dropdown.settings, parameters): $.extend({ } , $.fn.dropdown.settings), className = settings.className, message = settings.message, fields = settings.fields, keys = settings.keys, metadata = settings.metadata, namespace = settings.namespace, regExp = settings.regExp, selector = settings.selector, error = settings.error, templates = settings.templates, eventNamespace = '.' + namespace, moduleNamespace = 'module-' + namespace, $module = $(this), $context = $(settings.context), $text = $module.find(selector.text), $search = $module.find(_AN_Read_search('search', selector)), $sizer = $module.find(selector.sizer), $input = $module.find(selector.input), $icon = $module.find(selector.icon), $combo = (_AN_Read_length('length', $module.prev().find(selector.text)) > 0)? $module.prev().find(selector.text): $module.prev(), $menu = $module.children(selector.menu), $item = $menu.find(selector.item), activated = false , itemActivated = false , internalChange = false , element = this, instance = $module.data(moduleNamespace), initialLoad, pageLostFocus, willRefocus, elementNamespace, id, selectObserver, menuObserver, module; module = { initialize: function (){ module.debug('Initializing dropdown', settings); if (module.is.alreadySetup()) { module.setup.reference(); } else { module.setup.layout(); if (settings.values) { module.change.values(settings.values); } module.refreshData(); module.save.defaults(); module.restore.selected(); module.create.id(); module.bind.events(); module.observeChanges(); module.instantiate(); } } , instantiate: function (){ module.verbose('Storing instance of dropdown', module); instance = module; $module.data(moduleNamespace, module); } , destroy: function (){ module.verbose('Destroying previous dropdown', $module); module.remove.tabbable(); $module.off(eventNamespace).removeData(moduleNamespace); $menu.off(eventNamespace); $document.off(elementNamespace); module.disconnect.menuObserver(); module.disconnect.selectObserver(); } , observeChanges: function (){ if ('MutationObserver' in window) { selectObserver = new MutationObserver(module.event.select.mutation); menuObserver = new MutationObserver(module.event.menu.mutation); module.debug('Setting up mutation observer', selectObserver, menuObserver); module.observe.select(); module.observe.menu(); } } , disconnect: { menuObserver: function (){ if (menuObserver) { menuObserver.disconnect(); } } , selectObserver: function (){ if (selectObserver) { selectObserver.disconnect(); } } } , observe: { select: function (){ if (module.has.input()) { selectObserver.observe($module[0], { childList: true , subtree: true } ); } } , menu: function (){ if (module.has.menu()) { menuObserver.observe($menu[0], { childList: true , subtree: true } ); } } } , create: { id: function (){ id = (Math.random().toString(16) + '000000000').substr(2, 8); elementNamespace = '.' + id; module.verbose('Creating unique id for element', id); } , userChoice: function (values){ var $userChoices, $userChoice, isUserValue, html; values = values || module.get.userValues(); if (!values) { return false ; } values = $.isArray(values)? values: [values] ; $.each(values, function (index, value){ if (module.get.item(value) === false ) { html = settings.templates.addition(module.add.variables(message.addResult, value)); $userChoice = $('
').html(html).attr('data-' + metadata.value, value).attr('data-' + metadata.text, value).addClass(className.addition).addClass(className.item); if (settings.hideAdditions) { $userChoice.addClass(className.hidden); } $userChoices = ($userChoices === undefined)? $userChoice: $userChoices.add($userChoice); module.verbose('Creating user choices for value', value, $userChoice); } } ); return $userChoices; } , userLabels: function (value){ var userValues = module.get.userValues(); if (userValues) { module.debug('Adding user labels', userValues); $.each(userValues, function (index, value){ module.verbose('Adding custom user value'); module.add.label(value, value); } ); } } , menu: function (){ $menu = $('
').addClass(className.menu).appendTo($module); } , sizer: function (){ $sizer = $('').addClass(className.sizer).insertAfter($search); } } , search: function (query){ query = (query !== undefined)? query: module.get.query(); module.verbose('Searching for query', query); if (module.has.minCharacters(query)) { module.filter(query); } else { module.hide(); } } , select: { firstUnfiltered: function (){ module.verbose('Selecting first non-filtered element'); module.remove.selectedItem(); $item.not(selector.unselectable).not(selector.addition + selector.hidden).eq(0).addClass(className.selected); } , nextAvailable: function ($selected){ $selected = $selected.eq(0); var $nextAvailable = $selected.nextAll(selector.item).not(selector.unselectable).eq(0), $prevAvailable = $selected.prevAll(selector.item).not(selector.unselectable).eq(0), hasNext = (_AN_Read_length('length', $nextAvailable) > 0); if (hasNext) { module.verbose('Moving selection to', $nextAvailable); $nextAvailable.addClass(className.selected); } else { module.verbose('Moving selection to', $prevAvailable); $prevAvailable.addClass(className.selected); } } } , setup: { api: function (){ var apiSettings = { debug: settings.debug, urlData: { value: module.get.value(), query: module.get.query()} , on: false } ; module.verbose('First request, initializing API'); $module.api(apiSettings); } , layout: function (){ if ($module.is('select')) { module.setup.select(); module.setup.returnedObject(); } if (!module.has.menu()) { module.create.menu(); } if (module.is.search() && !module.has.search()) { module.verbose('Adding search input'); $search = $('').addClass(_AN_Read_search('search', className)).prop('autocomplete', 'off').insertBefore($text); } if (module.is.multiple() && module.is.searchSelection() && !module.has.sizer()) { module.create.sizer(); } if (settings.allowTab) { module.set.tabbable(); } } , select: function (){ var selectValues = module.get.selectValues(); module.debug('Dropdown initialized on a select', selectValues); if ($module.is('select')) { $input = $module; } if (_AN_Read_length('length', $input.parent(selector.dropdown)) > 0) { module.debug('UI dropdown already exists. Creating dropdown menu only'); $module = $input.closest(selector.dropdown); if (!module.has.menu()) { module.create.menu(); } $menu = $module.children(selector.menu); module.setup.menu(selectValues); } else { module.debug('Creating entire dropdown from select'); $module = $('
').attr('class', $input.attr('class')).addClass(className.selection).addClass(className.dropdown).html(templates.dropdown(selectValues)).insertBefore($input); if ($input.hasClass(className.multiple) && $input.prop('multiple') === false ) { module.error(error.missingMultiple); $input.prop('multiple', true ); } if ($input.is('[multiple]')) { module.set.multiple(); } if ($input.prop('disabled')) { module.debug('Disabling dropdown'); $module.addClass(className.disabled); } $input.removeAttr('class').detach().prependTo($module); } _AN_Call_refresh('refresh', module); } , menu: function (values){ $menu.html(templates.menu(values, fields)); $item = $menu.find(selector.item); } , reference: function (){ module.debug('Dropdown behavior was called on select, replacing with closest dropdown'); $module = $module.parent(selector.dropdown); instance = $module.data(moduleNamespace); element = $module.get(0); _AN_Call_refresh('refresh', module); module.setup.returnedObject(); } , returnedObject: function (){ var $firstModules = $allModules.slice(0, elementIndex), $lastModules = $allModules.slice(elementIndex + 1); $allModules = $firstModules.add($module).add($lastModules); } } , refresh: function (){ module.refreshSelectors(); module.refreshData(); } , refreshItems: function (){ $item = $menu.find(selector.item); } , refreshSelectors: function (){ module.verbose('Refreshing selector cache'); $text = $module.find(selector.text); $search = $module.find(_AN_Read_search('search', selector)); $input = $module.find(selector.input); $icon = $module.find(selector.icon); $combo = (_AN_Read_length('length', $module.prev().find(selector.text)) > 0)? $module.prev().find(selector.text): $module.prev(); $menu = $module.children(selector.menu); $item = $menu.find(selector.item); } , refreshData: function (){ module.verbose('Refreshing cached metadata'); $item.removeData(metadata.text).removeData(metadata.value); } , clearData: function (){ module.verbose('Clearing metadata'); $item.removeData(metadata.text).removeData(metadata.value); $module.removeData(metadata.defaultText).removeData(metadata.defaultValue).removeData(metadata.placeholderText); } , toggle: function (){ module.verbose('Toggling menu visibility'); if (!module.is.active()) { _AN_Call_show('show', module); } else { module.hide(); } } , show: function (callback){ callback = $.isFunction(callback)? callback: function (){ } ; if (!_AN_Call_show('show', module.can) && module.is.remote()) { module.debug('No API results retrieved, searching before show'); module.queryRemote(module.get.query(), module.show); } if (_AN_Call_show('show', module.can) && !module.is.active()) { module.debug('Showing dropdown'); if (module.has.message() && !(module.has.maxSelections() || module.has.allResultsFiltered())) { module.remove.message(); } if (module.is.allFiltered()) { return true ; } if (settings.onShow.call(element) !== false ) { _AN_Call_show('show', module.animate, function (){ if (module.can.click()) { module.bind.intent(); } if (module.has.menuSearch()) { module.focusSearch(); } module.set.visible(); callback.call(element); } ); } } } , hide: function (callback){ callback = $.isFunction(callback)? callback: function (){ } ; if (module.is.active()) { module.debug('Hiding dropdown'); if (settings.onHide.call(element) !== false ) { module.animate.hide(function (){ module.remove.visible(); callback.call(element); } ); } } } , hideOthers: function (){ module.verbose('Finding other dropdowns to hide'); $allModules.not($module).has(selector.menu + '.' + className.visible).dropdown('hide'); } , hideMenu: function (){ module.verbose('Hiding menu instantaneously'); module.remove.active(); module.remove.visible(); $menu.transition('hide'); } , hideSubMenus: function (){ var $subMenus = $menu.children(selector.item).find(selector.menu); module.verbose('Hiding sub menus', $subMenus); $subMenus.transition('hide'); } , bind: { events: function (){ if (hasTouch) { module.bind.touchEvents(); } module.bind.keyboardEvents(); module.bind.inputEvents(); module.bind.mouseEvents(); } , touchEvents: function (){ module.debug('Touch device detected binding additional touch events'); if (module.is.searchSelection()) { } else if (module.is.single()) { $module.on('touchstart' + eventNamespace, module.event.test.toggle); } $menu.on('touchstart' + eventNamespace, selector.item, module.event.item.mouseenter); } , keyboardEvents: function (){ module.verbose('Binding keyboard events'); $module.on('keydown' + eventNamespace, module.event.keydown); if (module.has.search()) { $module.on(module.get.inputEvent() + eventNamespace, _AN_Read_search('search', selector), module.event.input); } if (module.is.multiple()) { $document.on('keydown' + elementNamespace, module.event.document.keydown); } } , inputEvents: function (){ module.verbose('Binding input change events'); $module.on('change' + eventNamespace, selector.input, module.event.change); } , mouseEvents: function (){ module.verbose('Binding mouse events'); if (module.is.multiple()) { $module.on('click' + eventNamespace, selector.label, module.event.label.click).on('click' + eventNamespace, selector.remove, module.event.remove.click); } if (module.is.searchSelection()) { $module.on('mousedown' + eventNamespace, module.event.mousedown).on('mouseup' + eventNamespace, module.event.mouseup).on('mousedown' + eventNamespace, selector.menu, module.event.menu.mousedown).on('mouseup' + eventNamespace, selector.menu, module.event.menu.mouseup).on('click' + eventNamespace, selector.icon, module.event.icon.click).on('focus' + eventNamespace, _AN_Read_search('search', selector), _AN_Read_search('search', module.event).focus).on('click' + eventNamespace, _AN_Read_search('search', selector), _AN_Read_search('search', module.event).focus).on('blur' + eventNamespace, _AN_Read_search('search', selector), _AN_Read_search('search', module.event).blur).on('click' + eventNamespace, selector.text, module.event.text.focus); if (module.is.multiple()) { $module.on('click' + eventNamespace, module.event.click); } } else { if (settings.on == 'click') { $module.on('click' + eventNamespace, selector.icon, module.event.icon.click).on('click' + eventNamespace, module.event.test.toggle); } else if (settings.on == 'hover') { $module.on('mouseenter' + eventNamespace, module.delay.show).on('mouseleave' + eventNamespace, module.delay.hide); } else { $module.on(settings.on + eventNamespace, module.toggle); } $module.on('mousedown' + eventNamespace, module.event.mousedown).on('mouseup' + eventNamespace, module.event.mouseup).on('focus' + eventNamespace, module.event.focus); if (module.has.menuSearch()) { $module.on('blur' + eventNamespace, _AN_Read_search('search', selector), _AN_Read_search('search', module.event).blur); } else { $module.on('blur' + eventNamespace, module.event.blur); } } $menu.on('mouseenter' + eventNamespace, selector.item, module.event.item.mouseenter).on('mouseleave' + eventNamespace, selector.item, module.event.item.mouseleave).on('click' + eventNamespace, selector.item, module.event.item.click); } , intent: function (){ module.verbose('Binding hide intent event to document'); if (hasTouch) { $document.on('touchstart' + elementNamespace, module.event.test.touch).on('touchmove' + elementNamespace, module.event.test.touch); } $document.on('click' + elementNamespace, module.event.test.hide); } } , unbind: { intent: function (){ module.verbose('Removing hide intent event from document'); if (hasTouch) { $document.off('touchstart' + elementNamespace).off('touchmove' + elementNamespace); } $document.off('click' + elementNamespace); } } , filter: function (query){ var searchTerm = (query !== undefined)? query: module.get.query(), afterFiltered = function (){ if (module.is.multiple()) { module.filterActive(); } if (query || (!query && _AN_Read_length('length', module.get.activeItem()) == 0)) { module.select.firstUnfiltered(); } if (module.has.allResultsFiltered()) { if (settings.onNoResults.call(element, searchTerm)) { if (settings.allowAdditions) { if (settings.hideAdditions) { module.verbose('User addition with no menu, setting empty style'); module.set.empty(); module.hideMenu(); } } else { module.verbose('All items filtered, showing message', searchTerm); module.add.message(message.noResults); } } else { module.verbose('All items filtered, hiding dropdown', searchTerm); module.hideMenu(); } } else { module.remove.empty(); module.remove.message(); } if (settings.allowAdditions) { module.add.userSuggestion(query); } if (module.is.searchSelection() && _AN_Call_show('show', module.can) && module.is.focusedOnSearch()) { _AN_Call_show('show', module); } } ; if (settings.useLabels && module.has.maxSelections()) { return ; } if (settings.apiSettings) { if (module.can.useAPI()) { module.queryRemote(searchTerm, function (){ if (settings.filterRemoteData) { module.filterItems(searchTerm); } afterFiltered(); } ); } else { module.error(error.noAPI); } } else { module.filterItems(searchTerm); afterFiltered(); } } , queryRemote: function (query, callback){ var apiSettings = { errorDuration: false , cache: 'local', throttle: settings.throttle, urlData: { query: query} , onError: function (){ module.add.message(message.serverError); callback(); } , onFailure: function (){ module.add.message(message.serverError); callback(); } , onSuccess: function (response){ module.remove.message(); module.setup.menu({ values: response[fields.remoteValues]} ); callback(); } } ; if (!$module.api('get request')) { module.setup.api(); } apiSettings = $.extend(true , { } , apiSettings, settings.apiSettings); $module.api('setting', apiSettings).api('query'); } , filterItems: function (query){ var searchTerm = (query !== undefined)? query: module.get.query(), results = null , escapedTerm = module.escape.string(searchTerm), beginsWithRegExp = new RegExp('^' + escapedTerm, 'igm'); if (module.has.query()) { results = [] ; module.verbose('Searching for matching values', searchTerm); $item.each(function (){ var $choice = $(this), text, value; if (settings.match == 'both' || settings.match == 'text') { text = String(module.get.choiceText($choice, false )); if (text.search(beginsWithRegExp) !== -1) { results.push(this); return true ; } else if (settings.fullTextSearch === 'exact' && module.exactSearch(searchTerm, text)) { results.push(this); return true ; } else if (settings.fullTextSearch === true && module.fuzzySearch(searchTerm, text)) { results.push(this); return true ; } } if (settings.match == 'both' || settings.match == 'value') { value = String(module.get.choiceValue($choice, text)); if (value.search(beginsWithRegExp) !== -1) { results.push(this); return true ; } else if (settings.fullTextSearch === 'exact' && module.exactSearch(searchTerm, value)) { results.push(this); return true ; } else if (settings.fullTextSearch === true && module.fuzzySearch(searchTerm, value)) { results.push(this); return true ; } } } ); } module.debug('Showing only matched items', searchTerm); module.remove.filteredItem(); if (results) { $item.not(results).addClass(className.filtered); } } , fuzzySearch: function (query, term){ var termLength = _AN_Read_length('length', term), queryLength = _AN_Read_length('length', query); query = query.toLowerCase(); term = term.toLowerCase(); if (queryLength > termLength) { return false ; } if (queryLength === termLength) { return (query === term); } search: for (var characterIndex = 0, nextCharacterIndex = 0; characterIndex < queryLength; characterIndex++ ){ var queryCharacter = query.charCodeAt(characterIndex); while (nextCharacterIndex < termLength){ if (term.charCodeAt(nextCharacterIndex++ ) === queryCharacter) { continue search; } } return false ; } return true ; } , exactSearch: function (query, term){ query = query.toLowerCase(); term = term.toLowerCase(); if (term.indexOf(query) > -1) { return true ; } return false ; } , filterActive: function (){ if (settings.useLabels) { $item.filter('.' + className.active).addClass(className.filtered); } } , focusSearch: function (skipHandler){ if (module.has.search() && !module.is.focusedOnSearch()) { if (skipHandler) { $module.off('focus' + eventNamespace, _AN_Read_search('search', selector)); $search.focus(); $module.on('focus' + eventNamespace, _AN_Read_search('search', selector), _AN_Read_search('search', module.event).focus); } else { $search.focus(); } } } , forceSelection: function (){ var $currentlySelected = $item.not(className.filtered).filter('.' + className.selected).eq(0), $activeItem = $item.not(className.filtered).filter('.' + className.active).eq(0), $selectedItem = (_AN_Read_length('length', $currentlySelected) > 0)? $currentlySelected: $activeItem, hasSelected = (_AN_Read_length('length', $selectedItem) > 0); if (hasSelected && !module.is.multiple()) { module.debug('Forcing partial selection to selected item', $selectedItem); module.event.item.click.call($selectedItem, { } , true ); return ; } else { if (settings.allowAdditions) { module.set.selected(module.get.query()); module.remove.searchTerm(); } else { module.remove.searchTerm(); } } } , change: { values: function (values){ if (!settings.allowAdditions) { _AN_Call_clear('clear', module); } module.debug('Creating dropdown with specified values', values); module.setup.menu({ values: values} ); $.each(values, function (index, item){ if (item.selected == true ) { module.debug('Setting initial selection to', item.value); module.set.selected(item.value); return true ; } } ); } } , event: { change: function (){ if (!internalChange) { module.debug('Input changed, updating selection'); module.set.selected(); } } , focus: function (){ if (settings.showOnFocus && !activated && module.is.hidden() && !pageLostFocus) { _AN_Call_show('show', module); } } , blur: function (event){ pageLostFocus = (document.activeElement === this); if (!activated && !pageLostFocus) { module.remove.activeLabel(); module.hide(); } } , mousedown: function (){ if (module.is.searchSelection()) { willRefocus = true ; } else { activated = true ; } } , mouseup: function (){ if (module.is.searchSelection()) { willRefocus = false ; } else { activated = false ; } } , click: function (event){ var $target = $(_AN_Read_target('target', event)); if ($target.is($module)) { if (!module.is.focusedOnSearch()) { module.focusSearch(); } else { _AN_Call_show('show', module); } } } , search: { focus: function (){ activated = true ; if (module.is.multiple()) { module.remove.activeLabel(); } if (settings.showOnFocus) { module.search(); } } , blur: function (event){ pageLostFocus = (document.activeElement === this); if (module.is.searchSelection() && !willRefocus) { if (!itemActivated && !pageLostFocus) { if (settings.forceSelection) { module.forceSelection(); } module.hide(); } } willRefocus = false ; } } , icon: { click: function (event){ module.toggle(); } } , text: { focus: function (event){ activated = true ; module.focusSearch(); } } , input: function (event){ if (module.is.multiple() || module.is.searchSelection()) { module.set.filtered(); } clearTimeout(module.timer); module.timer = _AN_Call_settimeout('setTimeout', window, _AN_Read_search('search', module), _AN_Read_search('search', settings.delay)); } , label: { click: function (event){ var $label = $(this), $labels = $module.find(selector.label), $activeLabels = $labels.filter('.' + className.active), $nextActive = $label.nextAll('.' + className.active), $prevActive = $label.prevAll('.' + className.active), $range = (_AN_Read_length('length', $nextActive) > 0)? $label.nextUntil($nextActive).add($activeLabels).add($label): $label.prevUntil($prevActive).add($activeLabels).add($label); if (event.shiftKey) { $activeLabels.removeClass(className.active); $range.addClass(className.active); } else if (event.ctrlKey) { $label.toggleClass(className.active); } else { $activeLabels.removeClass(className.active); $label.addClass(className.active); } settings.onLabelSelect.apply(this, $labels.filter('.' + className.active)); } } , remove: { click: function (){ var $label = $(this).parent(); if ($label.hasClass(className.active)) { module.remove.activeLabels(); } else { module.remove.activeLabels($label); } } } , test: { toggle: function (event){ var toggleBehavior = (module.is.multiple())? module.show: module.toggle; if (module.is.bubbledLabelClick(event) || module.is.bubbledIconClick(event)) { return ; } if (module.determine.eventOnElement(event, toggleBehavior)) { event.preventDefault(); } } , touch: function (event){ module.determine.eventOnElement(event, function (){ if (event.type == 'touchstart') { module.timer = _AN_Call_settimeout('setTimeout', window, function (){ module.hide(); } , settings.delay.touch); } else if (event.type == 'touchmove') { clearTimeout(module.timer); } } ); event.stopPropagation(); } , hide: function (event){ module.determine.eventInModule(event, module.hide); } } , select: { mutation: function (mutations){ module.debug(' removing selected option', removedValue); newValue = module.remove.arrayValue(removedValue, values); module.remove.optionValue(removedValue); } else { module.verbose('Removing from delimited values', removedValue); newValue = module.remove.arrayValue(removedValue, values); newValue = newValue.join(settings.delimiter); } if (settings.fireOnInit === false && module.is.initialLoad()) { module.verbose('No callback on initial load', settings.onRemove); } else { settings.onRemove.call(element, removedValue, removedText, $removedItem); } module.set.value(newValue, removedText, $removedItem); module.check.maxSelections(); } , arrayValue: function (removedValue, values){ if (!$.isArray(values)) { values = [values] ; } values = $.grep(values, function (value){ return (removedValue != value); } ); module.verbose('Removed value from delimited string', removedValue, values); return values; } , label: function (value, shouldAnimate){ var $labels = $module.find(selector.label), $removedLabel = $labels.filter('[data-' + metadata.value + '="' + module.escape.string(value) + '"]'); module.verbose('Removing label', $removedLabel); $removedLabel.remove(); } , activeLabels: function ($activeLabels){ $activeLabels = $activeLabels || $module.find(selector.label).filter('.' + className.active); module.verbose('Removing active label selections', $activeLabels); module.remove.labels($activeLabels); } , labels: function ($labels){ $labels = $labels || $module.find(selector.label); module.verbose('Removing labels', $labels); $labels.each(function (){ var $label = $(this), value = $label.data(metadata.value), stringValue = (value !== undefined)? String(value): value, isUserValue = module.is.userValue(stringValue); if (settings.onLabelRemove.call($label, value) === false ) { module.debug('Label remove callback cancelled removal'); return ; } module.remove.message(); if (isUserValue) { module.remove.value(stringValue); module.remove.label(stringValue); } else { module.remove.selected(stringValue); } } ); } , tabbable: function (){ if (module.is.searchSelection()) { module.debug('Searchable dropdown initialized'); $search.removeAttr('tabindex'); $menu.removeAttr('tabindex'); } else { module.debug('Simple selection dropdown initialized'); $module.removeAttr('tabindex'); $menu.removeAttr('tabindex'); } } } , has: { menuSearch: function (){ return (module.has.search() && _AN_Read_length('length', $search.closest($menu)) > 0); } , search: function (){ return (_AN_Read_length('length', $search) > 0); } , sizer: function (){ return (_AN_Read_length('length', $sizer) > 0); } , selectInput: function (){ return ($input.is('select')); } , minCharacters: function (searchTerm){ if (settings.minCharacters) { searchTerm = (searchTerm !== undefined)? String(searchTerm): String(module.get.query()); return (_AN_Read_length('length', searchTerm) >= settings.minCharacters); } return true ; } , firstLetter: function ($item, letter){ var text, firstLetter; if (!$item || _AN_Read_length('length', $item) === 0 || typeof letter !== 'string') { return false ; } text = module.get.choiceText($item, false ); letter = letter.toLowerCase(); firstLetter = String(text).charAt(0).toLowerCase(); return (letter == firstLetter); } , input: function (){ return (_AN_Read_length('length', $input) > 0); } , items: function (){ return (_AN_Read_length('length', $item) > 0); } , menu: function (){ return (_AN_Read_length('length', $menu) > 0); } , message: function (){ return (_AN_Read_length('length', $menu.children(selector.message)) !== 0); } , label: function (value){ var escapedValue = module.escape.value(value), $labels = $module.find(selector.label); return (_AN_Read_length('length', $labels.filter('[data-' + metadata.value + '="' + module.escape.string(escapedValue) + '"]')) > 0); } , maxSelections: function (){ return (settings.maxSelections && module.get.selectionCount() >= settings.maxSelections); } , allResultsFiltered: function (){ var $normalResults = $item.not(selector.addition); return (_AN_Read_length('length', $normalResults.filter(selector.unselectable)) === _AN_Read_length('length', $normalResults)); } , userSuggestion: function (){ return (_AN_Read_length('length', $menu.children(selector.addition)) > 0); } , query: function (){ return (module.get.query() !== ''); } , value: function (value){ var values = module.get.values(), hasValue = $.isArray(values)? values && ($.inArray(value, values) !== -1): (values == value); return (hasValue)? true : false ; } } , is: { active: function (){ return $module.hasClass(className.active); } , bubbledLabelClick: function (event){ return $(_AN_Read_target('target', event)).is('select, input') && _AN_Read_length('length', $module.closest('label')) > 0; } , bubbledIconClick: function (event){ return _AN_Read_length('length', $(_AN_Read_target('target', event)).closest($icon)) > 0; } , alreadySetup: function (){ return ($module.is('select') && $module.parent(selector.dropdown).data(moduleNamespace) !== undefined && _AN_Read_length('length', $module.prev()) === 0); } , animating: function ($subMenu){ return ($subMenu)? $subMenu.transition && $subMenu.transition('is animating'): $menu.transition && $menu.transition('is animating'); } , leftward: function ($subMenu){ var $selectedMenu = $subMenu || $menu; return $selectedMenu.hasClass(className.leftward); } , disabled: function (){ return $module.hasClass(className.disabled); } , focused: function (){ return (document.activeElement === $module[0]); } , focusedOnSearch: function (){ return (document.activeElement === $search[0]); } , allFiltered: function (){ return ((module.is.multiple() || module.has.search()) && !(settings.hideAdditions == false && module.has.userSuggestion()) && !module.has.message() && module.has.allResultsFiltered()); } , hidden: function ($subMenu){ return !module.is.visible($subMenu); } , initialLoad: function (){ return initialLoad; } , inObject: function (needle, object){ var found = false ; $.each(object, function (index, property){ if (property == needle) { found = true ; return true ; } } ); return found; } , multiple: function (){ return $module.hasClass(className.multiple); } , remote: function (){ return settings.apiSettings && module.can.useAPI(); } , single: function (){ return !module.is.multiple(); } , selectMutation: function (mutations){ var selectChanged = false ; $.each(mutations, function (index, mutation){ if (_AN_Read_target('target', mutation) && $(_AN_Read_target('target', mutation)).is('select')) { selectChanged = true ; return true ; } } ); return selectChanged; } , search: function (){ return $module.hasClass(_AN_Read_search('search', className)); } , searchSelection: function (){ return (module.has.search() && _AN_Read_length('length', $search.parent(selector.dropdown)) === 1); } , selection: function (){ return $module.hasClass(className.selection); } , userValue: function (value){ return ($.inArray(value, module.get.userValues()) !== -1); } , upward: function ($menu){ var $element = $menu || $module; return $element.hasClass(className.upward); } , visible: function ($subMenu){ return ($subMenu)? $subMenu.hasClass(className.visible): $menu.hasClass(className.visible); } , verticallyScrollableContext: function (){ var overflowY = ($context.get(0) !== window)? $context.css('overflow-y'): false ; return (overflowY == 'auto' || overflowY == 'scroll'); } , horizontallyScrollableContext: function (){ var overflowX = ($context.get(0) !== window)? $context.css('overflow-X'): false ; return (overflowX == 'auto' || overflowX == 'scroll'); } } , can: { activate: function ($item){ if (settings.useLabels) { return true ; } if (!module.has.maxSelections()) { return true ; } if (module.has.maxSelections() && $item.hasClass(className.active)) { return true ; } return false ; } , openDownward: function ($subMenu){ var $currentMenu = $subMenu || $menu, canOpenDownward = true , onScreen = { } , calculations; $currentMenu.addClass(className.loading); calculations = { context: { scrollTop: $context.scrollTop(), height: $context.outerHeight()} , menu: { offset: $currentMenu.offset(), height: $currentMenu.outerHeight()} } ; if (module.is.verticallyScrollableContext()) { calculations.menu.offset.top += calculations.context.scrollTop; } onScreen = { above: (calculations.context.scrollTop) <= calculations.menu.offset.top - calculations.menu.height, below: (calculations.context.scrollTop + calculations.context.height) >= calculations.menu.offset.top + calculations.menu.height} ; if (onScreen.below) { module.verbose('Dropdown can fit in context downward', onScreen); canOpenDownward = true ; } else if (!onScreen.below && !onScreen.above) { module.verbose('Dropdown cannot fit in either direction, favoring downward', onScreen); canOpenDownward = true ; } else { module.verbose('Dropdown cannot fit below, opening upward', onScreen); canOpenDownward = false ; } $currentMenu.removeClass(className.loading); return canOpenDownward; } , openRightward: function ($subMenu){ var $currentMenu = $subMenu || $menu, canOpenRightward = true , isOffscreenRight = false , calculations; $currentMenu.addClass(className.loading); calculations = { context: { scrollLeft: $context.scrollLeft(), width: $context.outerWidth()} , menu: { offset: $currentMenu.offset(), width: $currentMenu.outerWidth()} } ; if (module.is.horizontallyScrollableContext()) { calculations.menu.offset.left += calculations.context.scrollLeft; } isOffscreenRight = (calculations.menu.offset.left + calculations.menu.width >= calculations.context.scrollLeft + calculations.context.width); if (isOffscreenRight) { module.verbose('Dropdown cannot fit in context rightward', isOffscreenRight); canOpenRightward = false ; } $currentMenu.removeClass(className.loading); return canOpenRightward; } , click: function (){ return (hasTouch || settings.on == 'click'); } , extendSelect: function (){ return settings.allowAdditions || settings.apiSettings; } , show: function (){ return !module.is.disabled() && (module.has.items() || module.has.message()); } , useAPI: function (){ return $.fn.api !== undefined; } } , animate: { show: function (callback, $subMenu){ var $currentMenu = $subMenu || $menu, start = ($subMenu)? function (){ } : function (){ module.hideSubMenus(); module.hideOthers(); module.set.active(); } , transition; callback = $.isFunction(callback)? callback: function (){ } ; module.verbose('Doing menu show animation', $currentMenu); module.set.direction($subMenu); transition = module.get.transition($subMenu); if (module.is.selection()) { module.set.scrollPosition(module.get.selectedItem(), true ); } if (module.is.hidden($currentMenu) || module.is.animating($currentMenu)) { if (transition == 'none') { start(); $currentMenu.transition('show'); callback.call(element); } else if ($.fn.transition !== undefined && $module.transition('is supported')) { $currentMenu.transition({ animation: transition + ' in', debug: settings.debug, verbose: settings.verbose, duration: settings.duration, queue: true , onStart: start, onComplete: function (){ callback.call(element); } } ); } else { module.error(error.noTransition, transition); } } } , hide: function (callback, $subMenu){ var $currentMenu = $subMenu || $menu, duration = ($subMenu)? (settings.duration * 0.9): settings.duration, start = ($subMenu)? function (){ } : function (){ if (module.can.click()) { module.unbind.intent(); } module.remove.active(); } , transition = module.get.transition($subMenu); callback = $.isFunction(callback)? callback: function (){ } ; if (module.is.visible($currentMenu) || module.is.animating($currentMenu)) { module.verbose('Doing menu hide animation', $currentMenu); if (transition == 'none') { start(); $currentMenu.transition('hide'); callback.call(element); } else if ($.fn.transition !== undefined && $module.transition('is supported')) { $currentMenu.transition({ animation: transition + ' out', duration: settings.duration, debug: settings.debug, verbose: settings.verbose, queue: true , onStart: start, onComplete: function (){ callback.call(element); } } ); } else { module.error(error.transition); } } } } , hideAndClear: function (){ module.remove.searchTerm(); if (module.has.maxSelections()) { return ; } if (module.has.search()) { module.hide(function (){ module.remove.filteredItem(); } ); } else { module.hide(); } } , delay: { show: function (){ module.verbose('Delaying show event to ensure user intent'); clearTimeout(module.timer); module.timer = _AN_Call_settimeout('setTimeout', window, module.show, settings.delay.show); } , hide: function (){ module.verbose('Delaying hide event to ensure user intent'); clearTimeout(module.timer); module.timer = _AN_Call_settimeout('setTimeout', window, module.hide, settings.delay.hide); } } , escape: { value: function (value){ var multipleValues = $.isArray(value), stringValue = (typeof value === 'string'), isUnparsable = (!stringValue && !multipleValues), hasQuotes = (stringValue && value.search(regExp.quote) !== -1), values = [] ; if (isUnparsable || !hasQuotes) { return value; } module.debug('Encoding quote values for use in select', value); if (multipleValues) { $.each(value, function (index, value){ values.push(_AN_Call_replace('replace', value, regExp.quote, '"')); } ); return values; } return _AN_Call_replace('replace', value, regExp.quote, '"'); } , string: function (text){ text = String(text); return _AN_Call_replace('replace', text, regExp.escape, '\\$&'); } } , setting: function (name, value){ module.debug('Changing setting', name, value); if ($.isPlainObject(name)) { $.extend(true , settings, name); } else if (value !== undefined) { if ($.isPlainObject(settings[name])) { $.extend(true , settings[name], value); } else { settings[name] = value; } } else { return settings[name]; } } , internal: function (name, value){ if ($.isPlainObject(name)) { $.extend(true , module, name); } else if (value !== undefined) { module[name] = value; } else { return module[name]; } } , debug: function (){ if (!settings.silent && settings.debug) { if (settings.performance) { module.performance.log(arguments); } else { module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':'); module.debug.apply(console, arguments); } } } , verbose: function (){ if (!settings.silent && settings.verbose && settings.debug) { if (settings.performance) { module.performance.log(arguments); } else { module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':'); module.verbose.apply(console, arguments); } } } , error: function (){ if (!settings.silent) { module.error = Function.prototype.bind.call(console.error, console, settings.name + ':'); module.error.apply(console, arguments); } } , performance: { log: function (message){ var currentTime, executionTime, previousTime; if (settings.performance) { currentTime = new Date().getTime(); previousTime = time || currentTime; executionTime = currentTime - previousTime; time = currentTime; performance.push({ 'Name': message[0], 'Arguments': [] .slice.call(message, 1) || '', 'Element': element, 'Execution Time': executionTime} ); } clearTimeout(module.performance.timer); module.performance.timer = _AN_Call_settimeout('setTimeout', window, module.performance.display, 500); } , display: function (){ var title = settings.name + ':', totalTime = 0; time = false ; clearTimeout(module.performance.timer); $.each(performance, function (index, data){ totalTime += data["Execution Time"] ; } ); title += ' ' + totalTime + 'ms'; if (moduleSelector) { title += ' \'' + moduleSelector + '\''; } if ((console.group !== undefined || console.table !== undefined) && _AN_Read_length('length', performance) > 0) { console.groupCollapsed(title); if (console.table) { console.table(performance); } else { $.each(performance, function (index, data){ console.log(data.Name + ': ' + data["Execution Time"] + 'ms'); } ); } console.groupEnd(); } performance = [] ; } } , invoke: function (query, passedArguments, context){ var object = instance, maxDepth, found, response; passedArguments = passedArguments || queryArguments; context = element || context; if (typeof query == 'string' && object !== undefined) { query = query.split(/[\. ]/); maxDepth = _AN_Read_length('length', query) - 1; $.each(query, function (depth, value){ var camelCaseValue = (depth != maxDepth)? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1): query; if ($.isPlainObject(object[camelCaseValue]) && (depth != maxDepth)) { object = object[camelCaseValue]; } else if (object[camelCaseValue] !== undefined) { found = object[camelCaseValue]; return false ; } else if ($.isPlainObject(object[value]) && (depth != maxDepth)) { object = object[value]; } else if (object[value] !== undefined) { found = object[value]; return false ; } else { module.error(error.method, query); return false ; } } ); } if ($.isFunction(found)) { response = found.apply(context, passedArguments); } else if (found !== undefined) { response = found; } if ($.isArray(returnedValue)) { returnedValue.push(response); } else if (returnedValue !== undefined) { returnedValue = [returnedValue, response] ; } else if (response !== undefined) { returnedValue = response; } return found; } } ; if (methodInvoked) { if (instance === undefined) { module.initialize(); } module.invoke(query); } else { if (instance !== undefined) { instance.invoke('destroy'); } module.initialize(); } } ); return (returnedValue !== undefined)? returnedValue: $allModules; } ; $.fn.dropdown.settings = { silent: false , debug: false , verbose: false , performance: true , on: 'click', action: 'activate', values: false , apiSettings: false , selectOnKeydown: true , minCharacters: 0, filterRemoteData: false , saveRemoteData: true , throttle: 200, context: window, direction: 'auto', keepOnScreen: true , match: 'both', fullTextSearch: false , placeholder: 'auto', preserveHTML: true , sortSelect: false , forceSelection: true , allowAdditions: false , hideAdditions: true , maxSelections: false , useLabels: true , delimiter: ',', showOnFocus: true , allowReselection: false , allowTab: true , allowCategorySelection: false , fireOnInit: false , transition: 'auto', duration: 200, glyphWidth: 1.037, label: { transition: 'scale', duration: 200, variation: false } , delay: { hide: 300, show: 200, search: 20, touch: 50} , onChange: function (value, text, $selected){ } , onAdd: function (value, text, $selected){ } , onRemove: function (value, text, $selected){ } , onLabelSelect: function ($selectedLabels){ } , onLabelCreate: function (value, text){ return $(this); } , onLabelRemove: function (value){ return true ; } , onNoResults: function (searchTerm){ return true ; } , onShow: function (){ } , onHide: function (){ } , name: 'Dropdown', namespace: 'dropdown', message: { addResult: 'Add {term}', count: '{count} selected', maxSelections: 'Max {maxCount} selections', noResults: 'No results found.', serverError: 'There was an error contacting the server'} , error: { action: 'You called a dropdown action that was not defined', alreadySetup: 'Once a select has been initialized behaviors must be called on the created ui dropdown', labels: 'Allowing user additions currently requires the use of labels.', missingMultiple: '