searchable-option-list-2.0.2.js revision b9fe3a5d4206480e6222c436da55c7409140b83f
/*
* SOL - Searchable Option List jQuery plugin
* Version 2.0.2
*
* Copyright 2015, Patrick Bauerochse
*
* Licensed under the MIT license:
*
*/
/*
* Modified by Krystof Tulinger 2016
*/
/*jslint nomen: true */
;
'use strict';
// constructor
this.$originalElement = $element;
// allow setting options as data attribute
// e.g. <select data-sol-options="{'allowNullSelection':true}">
};
// plugin prototype
selected: false, // boolean selected state
disabled: false, // boolean disabled state
},
disabled: false, // all children disabled boolean property
},
DATA_KEY: 'sol-element',
WINDOW_EVENTS_KEY: 'sol-window-events',
// default option values
defaults: {
name: undefined, // name attribute, can also be set as name="" attribute on original element or data-sol-name=""
texts: {
noItemsAvailable: 'No entries found',
selectAll: 'Select all',
selectNone: 'Select none',
quickDelete: '×',
searchplaceholder: 'Click here to search',
loadingData: 'Still loading data...',
/*
* Modified 2016
*/
itemsSelected: '{$a} more items selected'
},
events: {
onScroll: function () {
var selectionContainerYPos = this.$input.offset().top - this.config.scrollTarget.scrollTop() + this.$input.outerHeight(false),
displayContainerAboveInput = this.config.displayContainerAboveInput || document.documentElement.clientHeight - this.config.scrollTarget.scrollTop() < selectionContainerBottom,
selectionContainerWidth = this.$innerContainer.outerWidth(false) - parseInt(this.$selectionContainer.css('border-left-width'), 10) - parseInt(this.$selectionContainer.css('border-right-width'), 10);
if (displayContainerAboveInput) {
// position the popup above the input
selectionContainerYPos = this.$input.offset().top - selectionContainerHeight - this.config.scrollTarget.scrollTop() + parseInt(this.$selectionContainer.css('border-bottom-width'), 10);
this.$container
.removeClass('sol-selection-bottom')
.addClass('sol-selection-top');
} else {
this.$container
.removeClass('sol-selection-top')
.addClass('sol-selection-bottom');
}
// container has a certain width
// make selection container a bit wider
} else {
var borderRadiusSelector = displayContainerAboveInput ? 'border-bottom-right-radius' : 'border-top-right-radius';
// no border radius on top
this.$selectionContainer
if (this.$actionButtons) {
this.$actionButtons
}
}
this.$selectionContainer
// remember the position
}
},
showSelectAll: function () {
return this.config.multiple && this.config.selectAllMaxItemsThreshold && this.items && this.items.length <= this.config.selectAllMaxItemsThreshold;
},
useBracketParameters: false,
/*
* Modified 2016
*/
closeOnClick: false,
showSelectionBelowList: false,
allowNullSelection: false,
asyncBatchSize: 300,
searchTimeout: 300,
maxShow: 0
},
// initialize the plugin
init: function () {
var originalName = this._getNameAttribute(),
sol = this;
if (!originalName) {
this._showErrorLabel('name attribute is required');
return;
}
// old IE does not support trim
return this.replace(/^\s+|\s+$/g, '');
}
}
if (!this.config.scrollTarget) {
}
this._initializeUiElements();
this._initializeInputEvents();
setTimeout(function () {
// take original form element out of form submission
// by removing the name attribute
.removeAttr('name')
}, 0);
this.$originalElement.hide();
this.$container
.show();
return this;
},
_getNameAttribute: function () {
return this.config.name || this.$originalElement.data('sol-name') || this.$originalElement.attr('name');
},
// shows an error label
_showErrorLabel: function (message) {
if (!this.$container) {
} else {
}
},
// register click handler to determine when to trigger the close event
_registerWindowEventsIfNeccessary: function () {
if (!window[this.WINDOW_EVENTS_KEY]) {
// if clicked inside a sol element close all others
// else close all sol containers
if ($closestInnerContainer.length) {
} else if ($closestSelectionContainer.length) {
}
$('.sol-active')
$(item)
.close();
});
});
// remember we already registered the global events
window[this.WINDOW_EVENTS_KEY] = true;
}
},
// add sol ui elements
_initializeUiElements: function () {
var self = this;
this.internalScrollWrapper = function () {
}
};
this.$input = $('<input type="text"/>')
this.$noResultsItem = $('<div class="sol-no-results"/>').html(this.config.texts.noItemsAvailable).hide();
this.$xItemsSelected = $('<div class="sol-results-count"/>');
this.$caret = $('<div class="sol-caret-container"><b class="sol-caret"/></div>').click(function (e) {
e.preventDefault();
return false;
});
this.$innerContainer = $('<div class="sol-inner-container"/>').append($inputContainer).append(this.$caret);
this.$selection = $('<div class="sol-selection"/>');
this.$selectionContainer = $('<div class="sol-selection-container"/>')
.append(this.$noResultsItem)
.append(this.$loadingData)
.append(this.$selection);
this.$container = $('<div class="sol-container"/>')
.hide()
/*
* Modified 2016
*/
.keydown(function (e) {
if (e.keyCode == 13) {
var concat = '';
$("#sbox #qtbl input[type='text']").each(function () {
});
// follow the project user's typed (may not exist)
return false;
}
// follow the actual project
'/' +
return false;
}
// follow first selected project
return false;
}
return false;
}
return true;
}
})
.append(this.$selectionContainer)
.append(this.$innerContainer)
.insertBefore(this.$originalElement);
// add selected items display container
this.$showSelectionContainer = $('<div class="sol-current-selection"/>');
/*
* Modified 2016
*/
if (this.config.resultsContainer) {
} else {
if (this.config.showSelectionBelowList) {
} else {
}
}
// dimensions
}
// detect inline css classes and styles
cssClassList = [],
stylesList = [];
// apply css classes to $container
}
}
// apply css inline styles to $container
// height property, apply to innerContainer instead of outer
} else {
}
}
}
}
}
}
},
// set invisible to get original width setting instead of translated to px
if (domElement.currentStyle) {
} else if (window.getComputedStyle) {
}
},
_initializeInputEvents: function () {
// form event
var self = this,
var resetFunction = function () {
var $changedItems = [];
.trigger('sol-change', true);
}
});
}
};
// unfortunately the reset event gets fired _before_
// the inputs are actually reset. The only possibility
// to overcome this is to set an interval to execute
// own scripts some time after the actual reset event
// before fields are actually reset by the browser
// needed to reset newly checked fields
// timeout for selection after form reset
// needed to reset previously checked fields
setTimeout(function () {
}, 100);
});
}
// text input events
this.$input
.focus(function () {
})
.on('propertychange input', function (e) {
var valueChanged = true;
if (e.type=='propertychange') {
}
if (valueChanged) {
if ($(this).data('timeout')) {
}
}
});
// keyboard navigation
this.$container
.on('keydown', function (e) {
// event handling for keyboard navigation
// only when there are results to be shown
preventDefault = false,
// arrow up or down to select an item
self._setKeyBoardNavigationMode(true);
/*
* Modified 2016
*/
var indexOfNextHighlightedOption = $allVisibleOptions.index($currentHighlightedOption) + directionValue;
if (indexOfNextHighlightedOption < 0) {
}
.addClass('keyboard-selection');
/*
* Modified 2016
*/
/*
* Modified 2016
*/
//self.$selection.scrollTop(self.$selection.scrollTop() + $nextHighlightedOption.position().top);
preventDefault = true;
// toggle current selected item with space bar
.trigger('change');
preventDefault = true;
}
if (preventDefault) {
// dont trigger any events in the input
e.preventDefault();
return false;
}
}
})
.on('keyup', function (e) {
if (keyCode === 27) {
// escape key
if (self.keyboardNavigationMode === true) {
self._setKeyBoardNavigationMode(false);
// trigger closing of container
} else {
// reset input and result filter
}
// special events like shift and control
return;
}
});
},
_setKeyBoardNavigationMode: function (keyboardNavigationOn) {
if (keyboardNavigationOn) {
// on
this.keyboardNavigationMode = true;
} else {
// off
this.keyboardNavigationMode = false;
}
},
_applySearchTermFilter: function () {
return;
}
// show previously filtered elements again
this._setNoResultsItemVisible(false);
}
// call onScroll to position the popup again
// important if showing popup above list
}
},
return;
}
var self = this,
// reset keyboard navigation mode when applying new filter
this._setKeyBoardNavigationMode(false);
/*
* Modified 2016
* recursion was very slow (however good lookin')
*/
}
} else {
}
}
}
if (amountOfUnfilteredChildren === 0) {
}
}
}
},
_initializeData: function () {
this.items = this._detectDataFromOriginalElement();
} else {
this._showErrorLabel('Unknown data type');
}
if (this.items) {
// done right away -> invoke postprocessing
this._processDataItems(this.items);
}
},
_detectDataFromOriginalElement: function () {
var self = this,
solData = [];
if (itemTagName === 'option') {
if (solDataItem) {
}
} else if (itemTagName === 'optgroup') {
if (solDataItem) {
}
} else {
self._showErrorLabel('Invalid element found in select: ' + itemTagName + '. Only option and optgroup are allowed');
}
});
return this._invokeConverterIfNeccessary(solData);
return this._invokeConverterIfNeccessary(solDataAttributeValue);
} else {
this._showErrorLabel('Could not determine data from original element. Must be a select or data must be provided as data-sol-data="" attribute');
}
},
_processSelectOption: function ($option) {
return $.extend({}, this.SOL_OPTION_FORMAT, {
});
},
_processSelectOptgroup: function ($optgroup) {
var self = this,
children: []
}),
// explicitly disable children when optgroup is disabled
if (solOptiongroup.disabled) {
}
});
return solOptiongroup;
},
_fetchDataFromFunction: function (dataFunction) {
return this._invokeConverterIfNeccessary(dataFunction(this));
},
_fetchDataFromArray: function (dataArray) {
return this._invokeConverterIfNeccessary(dataArray);
},
_loadItemsFromUrl: function (url) {
var self = this;
success: function (actualData) {
}
},
},
dataType: 'json'
});
},
_invokeConverterIfNeccessary: function (dataItems) {
}
return dataItems;
},
_processDataItems: function (solItems) {
if (!solItems) {
this._showErrorLabel('Data items not present. Maybe the converter did not return any values');
return;
}
this._setNoResultsItemVisible(true);
this.$loadingData.remove();
return;
}
var self = this,
nextIndex = 0,
dataProcessedFunction = function () {
// hide "loading data"
this.$loadingData.remove();
this._initializeSelectAll();
}
},
loopFunction = function () {
var currentBatch = 0,
item;
} else {
return;
}
}
} else {
}
};
// start async rendering of html elements
loopFunction.call(this);
},
var self = this,
/*
* Modified 2016
*/
$labelText = $('<div class="sol-label-text"/>')
inputName = this._getNameAttribute();
/*
* Modified 2016
*/
$('<span>')
.addClass('pull-right important-note important-note-rounded')
.text('!')
);
}
// use checkboxes
$inputElement = $('<input type="checkbox" class="sol-checkbox"/>');
if (this.config.useBracketParameters) {
inputName += '[]';
}
} else {
// use radio buttons
$inputElement = $('<input type="radio" class="sol-radio"/>')
.on('change', function () {
// when selected notify all others of being deselected
self.$selectionContainer.find('input[type="radio"][name="' + inputName + '"]').not($(this)).trigger('sol-deselect');
})
.on('sol-deselect', function () {
// remove display selection item
// TODO also better show it inline instead of above or below to save space
self._removeSelectionDisplayItem($(this));
});
}
})
/*
* Modified 2016
*/
.find('.sol-option.keyboard-selection')
.removeClass("keyboard-selection")
//self.$selection.scrollTop(self.$selection.scrollTop() + $closestOption.position().top)
})
$label = $('<label class="sol-label"/>')
.append($labelText);
/*
* Modified 2016
*/
// go first project
}
/*
* Modified 2016
*/
}
},
_renderOptiongroup: function (solOptiongroup) {
var self = this,
$groupCaption = $('<div class="sol-optiongroup-label"/>')
if (solOptiongroup.disabled) {
}
/*
* Modified 2016
*/
$groupCaption.dblclick(function (e) {
// select all group
if (!e.ctrlKey) {
self.deselectAll();
}
}
});
/*
* Modified 2016
*/
});
}
},
_initializeSelectAll: function () {
// multiple values selectable
if (this.config.showSelectAll === true || ($.isFunction(this.config.showSelectAll) && this.config.showSelectAll.call(this))) {
// buttons for (de-)select all
var self = this,
$deselectAllButton = $('<a href="#" class="sol-deselect-all"/>').html(this.config.texts.selectNone).click(function (e) {
self.deselectAll();
e.preventDefault();
return false;
}),
$selectAllButton = $('<a href="#" class="sol-select-all"/>').html(this.config.texts.selectAll).click(function (e) {
e.preventDefault();
return false;
});
this.$actionButtons = $('<div class="sol-action-buttons"/>').append($selectAllButton).append($deselectAllButton).append('<div class="sol-clearfix"/>');
}
},
// apply state to original select if neccessary
// helps to keep old legacy code running which depends
// on retrieving the value via jQuery option selectors
// e.g. $('#myPreviousSelectWhichNowIsSol').val()
var self = this;
var $currentOriginalOption = $(item);
return;
}
});
}
} else {
}
// update position of selection container
// to allow selecting more entries
} else {
// only one option selectable
// close selection container
this.close();
}
}
},
_addSelectionDisplayItem: function ($changedItem) {
if (!$existingDisplayItem) {
/*
* Modified 2016
*/
label += ' <span class="important-note important-note-rounded" ';
label += 'title="Some message is important in this project.';
label += ' Find more info in the project list below.">!</span>'
}
$existingDisplayItem = $('<div class="sol-selected-display-item"/>')
.appendTo(this.$showSelectionContainer);
// show remove button on display items if not disabled and null selection allowed
$('<span class="sol-quick-delete"/>')
.click(function () {
.prop('checked', false)
.trigger('change');
})
}
/*
* Modified 2016
*/
var xitemstext = this.config.texts.itemsSelected.replace('{$a}', selected.length + 1 - this.config.maxShow);
this.$xItemsSelected.show();
}
}
},
_removeSelectionDisplayItem: function ($changedItem) {
if ($myDisplayItem) {
/*
* Modified 2016
*/
var xitemstext = this.config.texts.itemsSelected.replace('{$a}', selected.length - 1 - this.config.maxShow);
this.$xItemsSelected.show();
} else {
this.$xItemsSelected.hide();
}
.siblings('.sol-selected-display-item')
.not(":visible")
.not(this.$xItemsSelected)
.first()
.show();
}
}
},
_setNoResultsItemVisible: function (visible) {
if (visible) {
this.$noResultsItem.show();
this.$selection.hide();
if (this.$actionButtons) {
this.$actionButtons.hide();
}
} else {
this.$noResultsItem.hide();
this.$selection.show();
if (this.$actionButtons) {
this.$actionButtons.show();
}
}
},
isOpen: function () {
},
isClosed: function () {
return !this.isOpen();
},
toggle: function () {
if (this.isOpen()) {
this.close();
} else {
this.open();
}
},
open: function () {
if (this.isClosed()) {
}
}
},
close: function () {
if (this.isOpen()) {
this._setKeyBoardNavigationMode(false);
// reset search on close
this._applySearchTermFilter();
// clear to recalculate position again the next time sol is opened
}
}
},
/*
* Modified 2016
*/
: this.$selectionContainer
.find(".sol-optiongroup-label")
.closest('.sol-optiongroup')
.prop('checked', true)
.trigger('change', true);
}
}
},
/*
* Modified 2016
*/
invert: function() {
var $closedInputs = this.$selectionContainer
.find('input[type="checkbox"]:not([disabled], :checked)')
var $openedInputs = this.$selectionContainer
.trigger('change', true);
.trigger('change', true)
}
}
},
/*
* Modified 2016
*/
: this.$selectionContainer
.find(".sol-optiongroup-label")
.closest('.sol-optiongroup')
.prop('checked', false)
.trigger('change', true);
}
}
},
getSelection: function () {
}
};
// jquery plugin boiler plate code
var result = [];
this.each(function () {
var $this = $(this),
if ($alreadyInitializedSol) {
} else {
setTimeout(function() {
}, 0);
}
});
return result[0];
}
return result;
};