/*!
* # Fomantic-UI 2.9.0-beta.301+42e68bc - Dropdown
* http://github.com/fomantic/Fomantic-UI/
*
*
* Released under the MIT license
* http://opensource.org/licenses/MIT
*
*/
;(function ($, window, document, undefined) {
'use strict';
$.isFunction = $.isFunction || function(obj) {
return typeof obj === "function" && typeof obj.nodeType !== "number";
};
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 || '',
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 = [window,document].indexOf(settings.context) < 0 ? $(document).find(settings.context) : $(settings.context),
$text = $module.find(selector.text),
$search = $module.find(selector.search),
$sizer = $module.find(selector.sizer),
$input = $module.find(selector.input),
$icon = $module.find(selector.icon),
$clear = $module.find(selector.clearIcon),
$combo = ($module.prev().find(selector.text).length > 0)
? $module.prev().find(selector.text)
: $module.prev(),
$menu = $module.children(selector.menu),
$item = $menu.find(selector.item),
$divider = settings.hideDividers ? $item.parent().children(selector.divider) : $(),
activated = false,
itemActivated = false,
internalChange = false,
iconClicked = false,
element = this,
focused = false,
instance = $module.data(moduleNamespace),
selectActionActive,
initialLoad,
pageLostFocus,
willRefocus,
elementNamespace,
id,
selectObserver,
menuObserver,
classObserver,
module
;
module = {
initialize: function() {
module.debug('Initializing dropdown', settings);
if( module.is.alreadySetup() ) {
module.setup.reference();
}
else {
if (settings.ignoreDiacritics && !String.prototype.normalize) {
settings.ignoreDiacritics = false;
module.error(error.noNormalize, element);
}
module.setup.layout();
if(settings.values) {
module.set.initialLoad();
module.change.values(settings.values);
module.remove.initialLoad();
}
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.remove.active();
$menu.transition('stop all');
$menu.removeClass(className.visible).addClass(className.hidden);
$module
.off(eventNamespace)
.removeData(moduleNamespace)
;
$menu
.off(eventNamespace)
;
$document
.off(elementNamespace)
;
module.disconnect.menuObserver();
module.disconnect.selectObserver();
module.disconnect.classObserver();
},
observeChanges: function() {
if('MutationObserver' in window) {
selectObserver = new MutationObserver(module.event.select.mutation);
menuObserver = new MutationObserver(module.event.menu.mutation);
classObserver = new MutationObserver(module.event.class.mutation);
module.debug('Setting up mutation observer', selectObserver, menuObserver, classObserver);
module.observe.select();
module.observe.menu();
module.observe.class();
}
},
disconnect: {
menuObserver: function() {
if(menuObserver) {
menuObserver.disconnect();
}
},
selectObserver: function() {
if(selectObserver) {
selectObserver.disconnect();
}
},
classObserver: function() {
if(classObserver) {
classObserver.disconnect();
}
}
},
observe: {
select: function() {
if(module.has.input() && selectObserver) {
selectObserver.observe($module[0], {
attributes: true,
childList : true,
subtree : true
});
}
},
menu: function() {
if(module.has.menu() && menuObserver) {
menuObserver.observe($menu[0], {
childList : true,
subtree : true
});
}
},
class: function() {
if(module.has.search() && classObserver) {
classObserver.observe($module[0], {
attributes : true
});
}
}
},
create: {
id: function() {
id = (Math.random().toString(16) + '000000000').slice(2, 10);
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 = Array.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(settings.fireOnInit === false && module.is.initialLoad()) {
module.verbose('Skipping callback on initial load', settings.onSearch);
} else if(module.has.minCharacters(query) && settings.onSearch.call(element, query) !== false) {
module.filter(query);
}
else {
module.hide(null,true);
}
},
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 = ($nextAvailable.length > 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.clearable() && !module.has.clearItem() ) {
module.verbose('Adding clear icon');
$clear = $('')
.addClass('remove icon')
.insertBefore($text)
;
}
if( module.is.search() && !module.has.search() ) {
module.verbose('Adding search input');
$search = $('')
.addClass(className.search)
.prop('autocomplete', module.is.chrome() ? 'fomantic-search' : '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;
}
// see if select is placed correctly already
if($input.parent(selector.dropdown).length > 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, fields, settings.preserveHTML, settings.className) )
.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);
}
if($input.is('[required]')) {
settings.forceSelection = true;
}
$input
.removeAttr('required')
.removeAttr('class')
.detach()
.prependTo($module)
;
}
module.refresh();
},
menu: function(values) {
$menu.html( templates.menu(values, fields,settings.preserveHTML,settings.className));
$item = $menu.find(selector.item);
$divider = settings.hideDividers ? $item.parent().children(selector.divider) : $();
},
reference: function() {
module.debug('Dropdown behavior was called on select, replacing with closest dropdown');
// replace module reference
$module = $module.parent(selector.dropdown);
instance = $module.data(moduleNamespace);
element = $module.get(0);
module.refresh();
module.setup.returnedObject();
},
returnedObject: function() {
var
$firstModules = $allModules.slice(0, elementIndex),
$lastModules = $allModules.slice(elementIndex + 1)
;
// adjust all modules to use correct reference
$allModules = $firstModules.add($module).add($lastModules);
}
},
refresh: function() {
module.refreshSelectors();
module.refreshData();
},
refreshItems: function() {
$item = $menu.find(selector.item);
$divider = settings.hideDividers ? $item.parent().children(selector.divider) : $();
},
refreshSelectors: function() {
module.verbose('Refreshing selector cache');
$text = $module.find(selector.text);
$search = $module.find(selector.search);
$input = $module.find(selector.input);
$icon = $module.find(selector.icon);
$combo = ($module.prev().find(selector.text).length > 0)
? $module.prev().find(selector.text)
: $module.prev()
;
$menu = $module.children(selector.menu);
$item = $menu.find(selector.item);
$divider = settings.hideDividers ? $item.parent().children(selector.divider) : $();
},
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)
;
},
clearItems: function() {
$menu.empty();
module.refreshItems();
},
toggle: function() {
module.verbose('Toggling menu visibility');
if( !module.is.active() ) {
module.show();
}
else {
module.hide();
}
},
show: function(callback, preventFocus) {
callback = $.isFunction(callback)
? callback
: function(){}
;
if ((focused || iconClicked) && module.is.remote() && module.is.noApiCache()) {
module.clearItems();
}
if(!module.can.show() && module.is.remote()) {
module.debug('No API results retrieved, searching before show');
module.queryRemote(module.get.query(), module.show, [callback, preventFocus]);
}
if( module.can.show() && !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) {
module.animate.show(function() {
module.bind.intent();
if(module.has.search() && !preventFocus) {
module.focusSearch();
}
module.set.visible();
callback.call(element);
});
}
}
},
hide: function(callback, preventBlur) {
callback = $.isFunction(callback)
? callback
: function(){}
;
if( module.is.active() && !module.is.animatingOutward() ) {
module.debug('Hiding dropdown');
if(settings.onHide.call(element) !== false) {
module.animate.hide(function() {
module.remove.visible();
// hiding search focus
if ( module.is.focusedOnSearch() && preventBlur !== true ) {
$search.blur();
}
callback.call(element);
});
// Hide submenus explicitly. On some browsers (esp. mobile), they will not automatically receive a
// mouseleave event
var $subMenu = $module.find(selector.menu);
if($subMenu.length > 0) {
module.verbose('Hiding sub-menu', $subMenu);
$subMenu.each(function() {
module.animate.hide(false, $(this));
});
}
}
} else {
module.unbind.intent();
}
iconClicked = false;
focused = false;
},
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() {
module.bind.keyboardEvents();
module.bind.inputEvents();
module.bind.mouseEvents();
},
keyboardEvents: function() {
module.verbose('Binding keyboard events');
$module
.on('keydown' + eventNamespace, module.event.keydown)
;
if( module.has.search() ) {
$module
.on(module.get.inputEvent() + eventNamespace, selector.search, 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)
;
if(module.is.multiple() && module.is.searchSelection()) {
$module
.on('paste' + eventNamespace, selector.search, module.event.paste)
;
}
},
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('click' + eventNamespace, selector.clearIcon, module.event.clearIcon.click)
.on('focus' + eventNamespace, selector.search, module.event.search.focus)
.on('click' + eventNamespace, selector.search, module.event.search.focus)
.on('blur' + eventNamespace, selector.search, module.event.search.blur)
.on('click' + eventNamespace, selector.text, module.event.text.focus)
;
if(module.is.multiple()) {
$module
.on('click' + eventNamespace, module.event.click)
.on('click' + eventNamespace, module.event.search.focus)
;
}
}
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)
.on('touchstart' + eventNamespace, module.event.test.toggle)
.on('touchstart' + eventNamespace, selector.icon, module.event.icon.click)
;
}
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)
.on('click' + eventNamespace, selector.clearIcon, module.event.clearIcon.click)
;
if(module.has.menuSearch() ) {
$module
.on('blur' + eventNamespace, selector.search, module.event.search.blur)
;
}
else {
$module
.on('blur' + eventNamespace, module.event.blur)
;
}
}
$menu
.on('mouseenter' + eventNamespace, selector.item, module.event.item.mouseenter)
.on('touchstart' + 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');
$document
.on('click' + elementNamespace, module.event.test.hide)
;
}
},
unbind: {
intent: function() {
module.verbose('Removing hide intent event from document');
$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 && module.get.activeItem().length == 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(module.escape.htmlEntities(query));
}
if(module.is.searchSelection() && module.can.show() && module.is.focusedOnSearch() ) {
module.show();
}
}
;
if(settings.useLabels && module.has.maxSelections()) {
return;
}
if(settings.apiSettings) {
if( module.can.useAPI() ) {
module.queryRemote(searchTerm, function() {
if(settings.filterRemoteData) {
module.filterItems(searchTerm);
}
var preSelected = $input.val();
if(!Array.isArray(preSelected)) {
preSelected = preSelected && preSelected!=="" ? preSelected.split(settings.delimiter) : [];
}
if (module.is.multiple()) {
$.each(preSelected,function(index,value){
$item.filter('[data-value="'+value+'"]')
.addClass(className.filtered)
;
});
}
module.focusSearch(true);
afterFiltered();
});
}
else {
module.error(error.noAPI);
}
}
else {
module.filterItems(searchTerm);
afterFiltered();
}
},
queryRemote: function(query, callback, callbackParameters) {
if(!Array.isArray(callbackParameters)){
callbackParameters = [callbackParameters];
}
var
apiSettings = {
errorDuration : false,
cache : 'local',
throttle : settings.throttle,
urlData : {
query: query
}
},
apiCallbacks = {
onError: function(errorMessage, $module, xhr) {
module.add.message(message.serverError);
iconClicked = false;
focused = false;
callback.apply(null, callbackParameters);
if(typeof settings.apiSettings.onError === 'function') {
settings.apiSettings.onError.call(this, errorMessage, $module, xhr);
}
},
onFailure: function(response, $module, xhr) {
module.add.message(message.serverError);
iconClicked = false;
focused = false;
callback.apply(null, callbackParameters);
if(typeof settings.apiSettings.onFailure === 'function') {
settings.apiSettings.onFailure.call(this, response, $module, xhr);
}
},
onSuccess : function(response, $module, xhr) {
var
values = response[fields.remoteValues]
;
if (!Array.isArray(values)){
values = [];
}
module.remove.message();
var menuConfig = {};
menuConfig[fields.values] = values;
module.setup.menu(menuConfig);
if(values.length===0 && !settings.allowAdditions) {
module.add.message(message.noResults);
}
else {
var value = module.is.multiple() ? module.get.values() : module.get.value();
if (value !== '') {
module.verbose('Value(s) present after click icon, select value(s) in items');
module.set.selected(value, null, true, true);
}
}
iconClicked = false;
focused = false;
callback.apply(null, callbackParameters);
if(typeof settings.apiSettings.onSuccess === 'function') {
settings.apiSettings.onSuccess.call(this, response, $module, xhr);
}
}
}
;
if( !$module.api('get request') ) {
module.setup.api();
}
apiSettings = $.extend(true, {}, apiSettings, settings.apiSettings, apiCallbacks);
$module
.api('setting', apiSettings)
.api('query')
;
},
filterItems: function(query) {
var
searchTerm = module.remove.diacritics(query !== undefined
? query
: module.get.query()
),
results = null,
escapedTerm = module.escape.string(searchTerm),
regExpFlags = (settings.ignoreSearchCase ? 'i' : '') + 'gm',
beginsWithRegExp = new RegExp('^' + escapedTerm, regExpFlags)
;
// avoid loop if we're matching nothing
if( module.has.query() ) {
results = [];
module.verbose('Searching for matching values', searchTerm);
$item
.each(function(){
var
$choice = $(this),
text,
value
;
if($choice.hasClass(className.unfilterable)) {
results.push(this);
return true;
}
if(settings.match === 'both' || settings.match === 'text') {
text = module.remove.diacritics(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 = module.remove.diacritics(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)
;
}
if(!module.has.query()) {
$divider
.removeClass(className.hidden);
} else if(settings.hideDividers === true) {
$divider
.addClass(className.hidden);
} else if(settings.hideDividers === 'empty') {
$divider
.removeClass(className.hidden)
.filter(function() {
// First find the last divider in this divider group
// Dividers which are direct siblings are considered a group
var lastDivider = $(this).nextUntil(selector.item);
return (lastDivider.length ? lastDivider : $(this))
// Count all non-filtered items until the next divider (or end of the dropdown)
.nextUntil(selector.divider)
.filter(selector.item + ":not(." + className.filtered + ")")
// Hide divider if no items are found
.length === 0;
})
.addClass(className.hidden);
}
},
fuzzySearch: function(query, term) {
var
termLength = term.length,
queryLength = query.length
;
query = (settings.ignoreSearchCase ? query.toLowerCase() : query);
term = (settings.ignoreSearchCase ? term.toLowerCase() : term);
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 = (settings.ignoreSearchCase ? query.toLowerCase() : query);
term = (settings.ignoreSearchCase ? term.toLowerCase() : term);
return term.indexOf(query) > -1;
},
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, selector.search);
$search.focus();
$module.on('focus' + eventNamespace, selector.search, module.event.search.focus);
}
else {
$search.focus();
}
}
},
blurSearch: function() {
if( module.has.search() ) {
$search.blur();
}
},
forceSelection: function() {
var
$currentlySelected = $item.not(className.filtered).filter('.' + className.selected).eq(0),
$activeItem = $item.not(className.filtered).filter('.' + className.active).eq(0),
$selectedItem = ($currentlySelected.length > 0)
? $currentlySelected
: $activeItem,
hasSelected = ($selectedItem.length > 0)
;
if(settings.allowAdditions || (hasSelected && !module.is.multiple())) {
module.debug('Forcing partial selection to selected item', $selectedItem);
module.event.item.click.call($selectedItem, {}, true);
}
else {
module.remove.searchTerm();
}
},
change: {
values: function(values) {
if(!settings.allowAdditions) {
module.clear();
}
module.debug('Creating dropdown with specified values', values);
var menuConfig = {};
menuConfig[fields.values] = values;
module.setup.menu(menuConfig);
$.each(values, function(index, item) {
if(item.selected === true) {
module.debug('Setting initial selection to', item[fields.value]);
module.set.selected(item[fields.value]);
if(!module.is.multiple()) {
return false;
}
}
});
if(module.has.selectInput()) {
module.disconnect.selectObserver();
$input.html('');
$input.append('');
$.each(values, function(index, item) {
var
value = settings.templates.deQuote(item[fields.value]),
name = settings.templates.escape(
item[fields.name] || '',
settings.preserveHTML
)
;
$input.append('');
});
module.observe.select();
}
}
},
event: {
paste: function(event) {
var pasteValue = (event.originalEvent.clipboardData || window.clipboardData).getData('text'),
tokens = pasteValue.split(settings.delimiter)
;
tokens.forEach(function(value){
module.set.selected(module.escape.htmlEntities(value.trim()), null, true, true);
});
event.preventDefault();
},
change: function() {
if(!internalChange) {
module.debug('Input changed, updating selection');
module.set.selected();
}
},
focus: function() {
if(settings.showOnFocus && !activated && module.is.hidden() && !pageLostFocus) {
focused = true;
module.show();
}
},
blur: function(event) {
pageLostFocus = (document.activeElement === this);
if(!activated && !pageLostFocus) {
module.remove.activeLabel();
module.hide();
}
},
mousedown: function() {
if(module.is.searchSelection()) {
// prevent menu hiding on immediate re-focus
willRefocus = true;
}
else {
// prevents focus callback from occurring on mousedown
activated = true;
}
},
mouseup: function() {
if(module.is.searchSelection()) {
// prevent menu hiding on immediate re-focus
willRefocus = false;
}
else {
activated = false;
}
},
click: function(event) {
var
$target = $(event.target)
;
// focus search
if($target.is($module)) {
if(!module.is.focusedOnSearch()) {
module.focusSearch();
}
else {
module.show();
}
}
},
search: {
focus: function(event) {
activated = true;
if(module.is.multiple()) {
module.remove.activeLabel();
}
if(!focused && !module.is.active() && (settings.showOnFocus || (event.type !== 'focus' && event.type !== 'focusin')) && event.type !== 'touchstart') {
focused = true;
module.search();
}
},
blur: function(event) {
pageLostFocus = (document.activeElement === this);
if(module.is.searchSelection() && !willRefocus) {
if(!itemActivated && !pageLostFocus) {
if(settings.forceSelection) {
module.forceSelection();
} else if(!settings.allowAdditions){
module.remove.searchTerm();
}
module.hide();
}
}
willRefocus = false;
}
},
clearIcon: {
click: function(event) {
module.clear();
if(module.is.searchSelection()) {
module.remove.searchTerm();
}
module.hide();
event.stopPropagation();
}
},
icon: {
click: function(event) {
iconClicked=true;
if(module.has.search()) {
if(!module.is.active()) {
if(settings.showOnFocus){
module.focusSearch();
} else {
module.toggle();
}
} else {
module.blurSearch();
}
} else {
module.toggle();
}
event.stopPropagation();
}
},
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 = setTimeout(module.search, settings.delay.search);
},
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 = ($nextActive.length > 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));
event.stopPropagation();
}
},
remove: {
click: function(event) {
var
$label = $(this).parent()
;
if( $label.hasClass(className.active) ) {
// remove all selected labels
module.remove.activeLabels();
}
else {
// remove this label only
module.remove.activeLabels( $label );
}
event.stopPropagation();
}
},
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.is.multiple() || (module.is.multiple() && !module.is.active())) {
focused = true;
}
if( module.determine.eventOnElement(event, toggleBehavior) && event.type !== 'touchstart') {
// do not preventDefault of touchstart; so emulated mouseenter is triggered on first touch and not later
// (when selecting an item). The double-showing of the dropdown through both events does not hurt.
event.preventDefault();
}
},
hide: function(event) {
if(module.determine.eventInModule(event, module.hide)){
if(element.id && $(event.target).attr('for') === element.id){
event.preventDefault();
}
}
}
},
class: {
mutation: function(mutations) {
mutations.forEach(function(mutation) {
if(mutation.attributeName === "class") {
module.check.disabled();
}
});
}
},
select: {
mutation: function(mutations) {
if(module.is.selectMutation(mutations)) {
module.debug('