');
listElem.addClass(listClass);
$.each(itemsList, $.proxy(function(idx, item) {
if (item.type === 'option') {
listElem.append(this._buildListOptionElem(item));
} else if (item.type === 'optgroup') {
listElem.append(this._buildListOptGroupElem(item));
listElem.append(this._buildListElem(item.items, 'romo-option-list-optgroup'));
}
}, this));
return listElem;
}
RomoOptionListDropdown.prototype._buildListOptionElem = function(item) {
var itemElem = $('');
var value = item.value || '';
var displayText = item.displayText || '';
var displayHtml = item.displayHtml || item.displayText || ' '
itemElem.attr('data-romo-option-list-dropdown-option-value', value);
itemElem.attr('data-romo-option-list-dropdown-option-display-text', displayText);
itemElem.html(displayHtml);
if (item.selected === true) {
itemElem.addClass('selected');
this.prevValue = value; // the last option marked selected is used
}
if (item.disabled === true) {
itemElem.addClass('disabled');
}
return itemElem;
}
RomoOptionListDropdown.prototype._buildListOptGroupElem = function(item) {
var itemElem = $('');
itemElem.text(item.label);
return itemElem;
}
RomoOptionListDropdown.prototype._buildOptionFilter = function() {
var filter = $('');
if (this.elem.data('romo-option-list-dropdown-filter-placeholder') !== undefined) {
filter.attr('placeholder', this.elem.data('romo-option-list-dropdown-filter-placeholder'));
}
filter.attr('data-romo-indicator-text-input-elem-display', "block");
filter.attr('data-romo-indicator-text-input-indicator-position', "right");
if (this.elem.data('romo-option-list-dropdown-filter-indicator') !== undefined) {
filter.attr('data-romo-indicator-text-input-indicator', this.elem.data('romo-option-list-dropdown-filter-indicator'));
}
if (this.elem.data('romo-option-list-dropdown-filter-indicator-width-px') !== undefined) {
filter.attr('data-romo-indicator-text-input-indicator-width-px', this.elem.data('romo-option-list-dropdown-filter-indicator-width-px'));
}
filter.attr('data-romo-form-disable-enter-submit', "true");
filter.attr('data-romo-onkey-on', "keydown");
filter.attr('autocomplete', 'off');
return filter;
}
RomoOptionListDropdown.prototype._bindDropdownOptionFilter = function() {
this.optionFilterElem.romoIndicatorTextInput();
this.optionFilterElem.romoOnkey();
this.romoDropdown.elem.on('focus', $.proxy(function(e) {
if (this.blurTimeoutId !== undefined) {
clearTimeout(this.blurTimeoutId);
}
// remove any manual elem focus when elem is actually focused
this.optionFilterFocused = false;
this.romoDropdown.elem.removeClass(this.focusStyleClass);
if (this.openOnFocus === true) {
this.romoDropdown.elem.trigger('dropdown:triggerPopupOpen', []);
} else {
this.openOnFocus = this.elem.data('romo-option-list-dropdown-open-on-focus');
}
}, this));
this.romoDropdown.elem.on('blur', $.proxy(function(e) {
if (this.blurTimeoutId !== undefined) {
clearTimeout(this.blurTimeoutId);
}
// close the dropdown when elem is blurred
// remove any manual focus as well
this.romoDropdown.elem.removeClass(this.focusStyleClass);
this.blurTimeoutId = setTimeout($.proxy(function() {
if (this.popupMouseDown !== true && this.optionFilterFocused !== true) {
this.romoDropdown.elem.trigger('dropdown:triggerPopupClose', []);
}
}, this), 10);
}, this));
this.optionFilterElem.on('focus', $.proxy(function(e) {
if (this.blurTimeoutId !== undefined) {
clearTimeout(this.blurTimeoutId);
}
// manually make the elem focused when its filter is focused
this.optionFilterFocused = true;
this.romoDropdown.elem.addClass(this.focusStyleClass);
}, this));
this.optionFilterElem.on('blur', $.proxy(function(e) {
// remove any manual elem focus when its filter is blurred
this.optionFilterFocused = false;
this.romoDropdown.elem.removeClass(this.focusStyleClass);
}, this));
this.romoDropdown.elem.on('dropdown:popupOpen', $.proxy(function(e, dropdown) {
this.optionFilterElem.trigger('indicatorTextInput:triggerPlaceIndicator');
this.optionFilterElem.focus();
this._filterOptionElems();
this.openOnFocus = false;
}, this));
this.romoDropdown.elem.on('dropdown:popupClose', $.proxy(function(e, dropdown) {
this.optionFilterElem.val('');
/*
don't call `_filterOptionElems()` here. we need to keep the option markup as is
until the popup is opened again so selecting an option works. selecting an option
depends on the selected item elem method which requires the markup to be in place
*/
}, this));
this.romoDropdown.elem.on('dropdown:popupClosedByEsc', $.proxy(function(e, dropdown) {
this.romoDropdown.elem.focus();
}, this));
this.optionFilterElem.on('click', $.proxy(function(e) {
if (e !== undefined) {
e.stopPropagation();
}
}, this));
this.romoDropdown.popupElem.on('click', $.proxy(function(e) {
this.optionFilterElem.focus();
}, this));
this.onkeySearchTimeout = undefined;
this.onkeySearchDelay = 100; // 0.1 secs, want it to be really responsive
this.optionFilterElem.on('onkey:trigger', $.proxy(function(e, triggerEvent, onkey) {
// TODO: incorp this timeout logic into the onkey component so don't have to repeat it
clearTimeout(this.onkeySearchTimeout);
this.onkeySearchTimeout = setTimeout($.proxy(function() {
if (Romo.nonInputTextKeyCodes().indexOf(triggerEvent.keyCode) === -1 /* Input Text */) {
this._filterOptionElems();
}
}, this), this.onkeySearchDelay);
}, this));
}
RomoOptionListDropdown.prototype._filterOptionElems = function() {
this.elem.trigger('romoOptionListDropdown:filterChange', [this.optionFilterElem.val(), this]);
}
RomoOptionListDropdown.prototype._updateOptionsListUI = function(highlightOptionElem) {
this.romoDropdown.doPlacePopupElem();
if (highlightOptionElem !== undefined) {
this._highlightItem(highlightOptionElem);
this._scrollTopToItem(highlightOptionElem);
} else {
this._highlightItem(this.selectedItemElem());
this._scrollTopToItem(this.selectedItemElem());
}
}
RomoOptionListDropdown.prototype._selectHighlightedItem = function() {
var curr = this._getHighlightedItemElem();
if (curr.length !== 0) {
var prevValue = this.prevValue;
var newValue = curr.data('romo-option-list-dropdown-option-value');
var newText = curr.data('romo-option-list-dropdown-option-display-text');
this.romoDropdown.doPopupClose();
this.elem.trigger('romoOptionListDropdown:itemSelected', [newValue, newText, this]);
if (newValue !== prevValue) {
this.doSetSelectedItem(newValue);
// always publish the item selected events before publishing any change events
this.elem.trigger('romoOptionListDropdown:newItemSelected', [newValue, newText, this]);
this.elem.trigger('romoOptionListDropdown:change', [newValue, prevValue, this]);
}
}
}
RomoOptionListDropdown.prototype._onPopupOpen = function(e) {
if (this.elem.hasClass('disabled') === false) {
this._highlightItem(this.selectedItemElem());
this._scrollTopToItem(this.selectedItemElem());
}
$('body').on('keydown', $.proxy(this._onPopupOpenBodyKeyDown, this));
}
RomoOptionListDropdown.prototype._onPopupClose = function(e) {
this._highlightItem($());
$('body').off('keydown', $.proxy(this._onPopupOpenBodyKeyDown, this));
}
RomoOptionListDropdown.prototype._onItemEnter = function(e) {
if (e !== undefined) {
e.preventDefault();
e.stopPropagation();
}
this._highlightItem($(e.target));
}
RomoOptionListDropdown.prototype._onItemClick = function(e) {
if (this.blurTimeoutId !== undefined) {
clearTimeout(this.blurTimeoutId);
this.blurTimeoutId = undefined;
}
if (e !== undefined) {
e.preventDefault();
e.stopPropagation();
}
this._selectHighlightedItem();
}
RomoOptionListDropdown.prototype._onPopupMouseDown = function(e) {
this.popupMouseDown = true;
}
RomoOptionListDropdown.prototype._onPopupMouseUp = function(e) {
this.popupMouseDown = false;
}
RomoOptionListDropdown.prototype._onPopupOpenBodyKeyDown = function(e) {
if (e !== undefined) {
e.stopPropagation();
}
var scroll = this.romoDropdown.bodyElem;
if (e.keyCode === 38 /* Up */) {
var prev = this._prevListItem();
this._highlightItem(prev);
if (scroll.offset().top > prev.offset().top) {
this._scrollTopToItem(prev);
} else if ((scroll.offset().top + scroll.height()) < prev.offset().top) {
this._scrollTopToItem(prev);
}
return false;
} else if(e.keyCode === 40 /* Down */) {
var next = this._nextListItem();
this._highlightItem(next);
if ((scroll.offset().top + scroll.height()) < next.offset().top + next.height()) {
this._scrollBottomToItem(next);
} else if (scroll.offset().top > next.offset().top) {
this._scrollTopToItem(next);
}
return false;
} else if (e.keyCode === 13 /* Enter */ ) {
this._selectHighlightedItem();
return false;
} else if (e.keyCode === 9 /* Tab */ ) {
e.preventDefault();
return false;
} else {
return true;
}
}
RomoOptionListDropdown.prototype._onElemKeyDown = function(e) {
if (this.elem.hasClass('disabled') === false) {
if (this.romoDropdown.popupClosed()) {
if (e.keyCode === 40 /* Down */ || e.keyCode === 38 /* Up */) {
this.romoDropdown.doPopupOpen();
return false;
} else if (this.optionFilterElem !== undefined &&
Romo.nonInputTextKeyCodes().indexOf(e.keyCode) === -1 /* Input Text */) {
// don't prevent default on Cmd-* keys (preserve Cmd-R refresh, etc)
if (e.metaKey === false) {
e.preventDefault();
if (e.keyCode !== 8) { /* Backspace */
this.optionFilterElem.val(e.key);
}
this.romoDropdown.doPopupOpen();
}
e.stopPropagation();
return true;
} else {
return true;
}
}
}
return true;
}
RomoOptionListDropdown.prototype._scrollTopToItem = function(item) {
if (item.size() > 0) {
var scroll = this.romoDropdown.bodyElem;
scroll.scrollTop(0);
var scrollOffsetTop = scroll.offset().top;
var selOffsetTop = item.offset().top;
var selOffset = item.height() / 2;
scroll.scrollTop(selOffsetTop - scrollOffsetTop - selOffset);
}
}
RomoOptionListDropdown.prototype._scrollBottomToItem = function(item) {
if (item.size() > 0) {
var scroll = this.romoDropdown.bodyElem;
scroll.scrollTop(0);
var scrollOffsetTop = scroll.offset().top;
var selOffsetTop = item.offset().top;
var selOffset = scroll[0].offsetHeight - item.height();
scroll.scrollTop(selOffsetTop - scrollOffsetTop - selOffset);
}
}
RomoOptionListDropdown.prototype._nextListItem = function() {
var curr = this._getHighlightedItemElem();
if (curr.length === 0) {
return curr;
}
var next = Romo.selectNext(curr, this.itemSelector+', UL.romo-option-list-optgroup');
if (next.length === 0) {
// curr is either the last ungrouped opt elem in the overall
// list OR is the last opt elem in a grouped list.
// if the hightlighted opt elem is in an opt group list, use
// its list as the reference elem. otherwise keep using the
// hightlighted opt elem itself.
curr = curr.closest('UL.romo-option-list-optgroup') || curr;
next = Romo.selectNext(curr, this.itemSelector+', UL.romo-option-list-optgroup');
}
while (next.hasClass('romo-option-list-optgroup') && next.children().size() === 0) {
// keep trying until you find a opt group list with options or an option or nothing
curr = next;
next = Romo.selectNext(curr, this.itemSelector+', UL.romo-option-list-optgroup');
}
if (next.length === 0) {
// curr is the last opt elem (grouped or not) in the overall
// list. get the the first opt elem in the overall list
next = this.romoDropdown.bodyElem.find(this.itemSelector).first();
} else if (next.hasClass('romo-option-list-optgroup')) {
// curr (grouped or not) is before an opt group list. get
// the first opt elem in that list.
next = next.find(this.itemSelector).first();
}
// otherwise curr (grouped or not) is before an opt elem.
// use that opt elem.
return next;
}
RomoOptionListDropdown.prototype._prevListItem = function() {
var curr = this._getHighlightedItemElem();
if (curr.length === 0) {
return curr;
}
var prev = Romo.selectPrev(curr, this.itemSelector+', UL.romo-option-list-optgroup');
if (prev.length === 0) {
// curr is either the first ungrouped opt elem in the overall
// list OR is the first opt elem in a grouped list
// if the hightlighted opt elem is in an opt group list, use
// its list as the reference elem. otherwise keep using the
// hightlighted opt elem itself.
curr = curr.closest('UL.romo-option-list-optgroup') || curr;
prev = Romo.selectPrev(curr, this.itemSelector+', UL.romo-option-list-optgroup');
}
while (prev.hasClass('romo-option-list-optgroup') && prev.children().size() === 0) {
// keep trying until you find a opt group list with options or an option or nothing
curr = prev;
prev = Romo.selectPrev(curr, this.itemSelector+', UL.romo-option-list-optgroup');
}
if (prev.length === 0) {
// curr is the first opt elem (grouped or not) in the overall
// list. get the the last opt elem in the overall list
prev = this.romoDropdown.bodyElem.find(this.itemSelector).last();
} else if (prev.hasClass('romo-option-list-optgroup')) {
// curr (grouped or not) is after an opt group list. get
// the last opt elem in that list.
prev = prev.find(this.itemSelector).last();
}
// otherwise curr (grouped or not) is after an opt elem.
// use that opt elem.
return prev;
}
RomoOptionListDropdown.prototype._highlightItem = function(item) {
this._getHighlightedItemElem().removeClass('romo-option-list-dropdown-highlight');
item.addClass('romo-option-list-dropdown-highlight');
}
RomoOptionListDropdown.prototype._getHighlightedItemElem = function() {
return this.romoDropdown.bodyElem.find('LI.romo-option-list-dropdown-highlight');
}
Romo.onInitUI(function(e) {
Romo.initUIElems(e, '[data-romo-option-list-dropdown-auto="true"]').romoOptionListDropdown();
});