Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
META-INF.resources.org.richfaces.Autocomplete.js Maven / Gradle / Ivy
(function ($, rf) {
/*
* TODO: add user's event handlers call from options
* TODO: add fire events
*/
rf.ui = rf.ui || {};
/**
* Backing object for rich:autocomplete
*
* @extends RichFaces.ui.AutocompleteBase
* @memberOf! RichFaces.ui
* @constructs RichFaces.ui.Autocomplete
*
* @param componentId {string} component id
* @param fieldId {string} id of the input box
* @param options {Object} autocomplete options
*/
// Constructor definition
rf.ui.Autocomplete = function(componentId, fieldId, options) {
this.namespace = "." + rf.Event.createNamespace(this.name, componentId);
this.options = {};
// call constructor of parent class
$super.constructor.call(this, componentId, componentId + ID.SELECT, fieldId, options);
this.attachToDom();
this.options = $.extend(this.options, defaultOptions, options);
this.value = "";
this.index = null;
this.isFirstAjax = true;
updateTokenOptions.call(this);
bindEventHandlers.call(this);
updateItemsList.call(this, "");
};
// Extend component class and add protected methods from parent class to our container
rf.ui.AutocompleteBase.extend(rf.ui.Autocomplete);
// define super class link
var $super = rf.ui.Autocomplete.$super;
var defaultOptions = {
itemClass:'rf-au-itm',
selectedItemClass:'rf-au-itm-sel',
subItemClass:'rf-au-opt',
selectedSubItemClass:'rf-au-opt-sel',
autofill:true,
minChars:1,
selectFirst:true,
ajaxMode:true,
lazyClientMode:false,
isCachedAjax:true,
tokens: "",
attachToBody:true,
filterFunction: undefined
//nothingLabel = "Nothing";
};
var ID = {
SELECT:'List',
ITEMS:'Items',
VALUE:'Value'
};
var REGEXP_TRIM = /^[\n\s]*(.*)[\n\s]*$/;
var getData = function (nodeList) {
var data = [];
nodeList.each(function () {
data.push($(this).text().replace(REGEXP_TRIM, "$1"));
});
return data;
}
var updateTokenOptions = function () {
this.useTokens = (typeof this.options.tokens == "string" && this.options.tokens.length > 0);
if (this.useTokens) {
var escapedTokens = this.options.tokens.split('').join("\\");
this.REGEXP_TOKEN_RIGHT = new RegExp('[' + escapedTokens + ']', 'i');
this.getLastTokenIndex = function(value) {
return RichFaces.ui.Autocomplete.__getLastTokenIndex(escapedTokens, value);
}
}
};
var bindEventHandlers = function () {
var list = $(rf.getDomElement(this.id + ID.ITEMS).parentNode);
list.on("click" + this.namespace, "."+this.options.itemClass, $.proxy(onMouseClick, this));
// The mouseenter event is available only natively in Internet Explorer, however jQuery emulates it in other browsers
list.on("mouseenter" + this.namespace, "."+this.options.itemClass, $.proxy(onMouseEnter, this));
};
var onMouseEnter = function(event) {
var element = $(event.target);
if (element && !element.hasClass(this.options.itemClass)) {
element = element.parents("." + this.options.itemClass).get(0);
}
if (element) {
var index = this.items.index(element);
selectItem.call(this, event, index);
}
};
var onMouseClick = function(event) {
var element = $(event.target);
if (element && !element.hasClass(this.options.itemClass)) {
element = element.parents("." + this.options.itemClass).get(0);
}
if (element) {
this.__onEnter(event);
rf.Selection.setCaretTo(rf.getDomElement(this.fieldId));
this.__hide(event);
}
};
var updateItemsList = function (value, fetchValues) {
var itemsContainer = $(rf.getDomElement(this.id + ID.ITEMS));
this.items = itemsContainer.find("." + this.options.itemClass);
var componentData = itemsContainer.data("componentData");
itemsContainer.removeData("componentData");
if (this.items.length > 0) {
this.cache = new rf.utils.Cache((this.options.ajaxMode ? value : ""), this.items, fetchValues || componentData || getData, !this.options.ajaxMode);
}
};
var scrollToSelectedItem = function() {
var offset = 0;
this.items.slice(0, this.index).each(function() {
offset += this.offsetHeight;
});
var parentContainer = $(rf.getDomElement(this.id + ID.ITEMS)).parent();
if (offset < parentContainer.scrollTop()) {
parentContainer.scrollTop(offset);
} else {
offset += this.items.eq(this.index).outerHeight();
if (offset - parentContainer.scrollTop() > parentContainer.innerHeight()) {
parentContainer.scrollTop(offset - parentContainer.innerHeight());
}
}
};
var autoFill = function (inputValue, value) {
if (this.options.autofill && value.toLowerCase().indexOf(inputValue.toLowerCase()) == 0) {
var field = rf.getDomElement(this.fieldId);
var start = rf.Selection.getStart(field);
this.__setInputValue(inputValue + value.substring(inputValue.length));
var end = start + value.length - inputValue.length;
rf.Selection.set(field, start, end);
}
};
var callAjax = function(event, callback) {
rf.getDomElement(this.id + ID.VALUE).value = this.value;
var _this = this;
var _event = event;
var ajaxSuccess = function (event) {
if (_this.options.minChars <= _this.value.length) {
updateItemsList.call(_this, _this.value, event.componentData && event.componentData[_this.id]);
}
if (_this.options.lazyClientMode && _this.value.length != 0) {
updateItemsFromCache.call(_this, _this.value);
}
if (_this.items.length != 0) {
if (callback) {
(_this.focused || _this.isMouseDown) && callback.call(_this, _event);
} else {
_this.isVisible && _this.options.selectFirst && selectItem.call(_this, _event, 0);
}
} else {
_this.__hide(_event);
}
};
var ajaxError = function (event) {
_this.__hide(_event);
clearItems.call(_this);
};
this.isFirstAjax = false;
//caution: JSF submits inputs with empty names causing "WARNING: Parameters: Invalid chunk ignored." in Tomcat log
var params = {};
params[this.id + ".ajax"] = "1";
var opts = {
parameters: params,
error: ajaxError,
complete:ajaxSuccess,
queueId: _this.options.queueId,
begin: _this.options.onbegin,
status: _this.options.status
};
rf.ajax(this.id, event, opts);
};
var clearSelection = function () {
if (this.index != null) {
var element = this.items.eq(this.index);
if (element.removeClass(this.options.selectedItemClass).hasClass(this.options.subItemClass)) {
element.removeClass(this.options.selectedSubItemClass);
}
this.index = null;
}
};
var selectItem = function(event, index, isOffset) {
if (this.items.length == 0 || (!isOffset && index == this.index)) return;
if (index == null || index == undefined) {
clearSelection.call(this);
return;
}
if (isOffset) {
if (this.index == null) {
index = 0;
} else {
index = this.index + index;
}
}
if (index < 0) {
index = 0;
} else if (index >= this.items.length) {
index = this.items.length - 1;
}
if (index == this.index) return;
clearSelection.call(this);
this.index = index;
var item = this.items.eq(this.index);
if (item.addClass(this.options.selectedItemClass).hasClass(this.options.subItemClass)) {
item.addClass(this.options.selectedSubItemClass);
}
scrollToSelectedItem.call(this);
if (event &&
event.keyCode != rf.KEYS.BACKSPACE &&
event.keyCode != rf.KEYS.DEL &&
event.keyCode != rf.KEYS.LEFT &&
event.keyCode != rf.KEYS.RIGHT) {
autoFill.call(this, this.value, getSelectedItemValue.call(this));
}
};
var updateItemsFromCache = function (value) {
var newItems = this.cache.getItems(value, this.options.filterFunction);
this.items = $(newItems);
//TODO: works only with simple markup, not with
$(rf.getDomElement(this.id + ID.ITEMS)).empty().append(this.items);
};
var clearItems = function () {
$(rf.getDomElement(this.id + ID.ITEMS)).removeData().empty();
this.items = [];
};
var onChangeValue = function (event, value, callback) {
selectItem.call(this, event);
// value is undefined if called from AutocompleteBase onChange
var subValue = (typeof value == "undefined") ? this.__getSubValue() : value;
var oldValue = this.value;
this.value = subValue;
if ((this.options.isCachedAjax || !this.options.ajaxMode) &&
this.cache && this.cache.isCached(subValue)) {
if (oldValue != subValue) {
updateItemsFromCache.call(this, subValue);
}
if (this.items.length != 0) {
callback && callback.call(this, event);
} else {
this.__hide(event);
}
if (event.keyCode == rf.KEYS.RETURN || event.type == "click") {
this.__setInputValue(subValue);
} else if (this.options.selectFirst) {
selectItem.call(this, event, 0);
}
} else {
if (event.keyCode == rf.KEYS.RETURN || event.type == "click") {
this.__setInputValue(subValue);
}
if (subValue.length >= this.options.minChars) {
if ((this.options.ajaxMode || this.options.lazyClientMode) && (oldValue != subValue || (oldValue === '' && subValue === ''))) {
callAjax.call(this, event, callback);
}
} else {
if (this.options.ajaxMode) {
clearItems.call(this);
this.__hide(event);
}
}
}
};
var getSelectedItemValue = function () {
if (this.index != null) {
var element = this.items.eq(this.index);
return this.cache.getItemValue(element);
}
return undefined;
};
var getSubValue = function () {
//TODO: add posibility to use space chars before and after tokens if space not a token char
if (this.useTokens) {
var field = rf.getDomElement(this.fieldId);
var value = field.value;
var cursorPosition = rf.Selection.getStart(field);
var beforeCursorStr = value.substring(0, cursorPosition);
var afterCursorStr = value.substring(cursorPosition);
var result = beforeCursorStr.substring(this.getLastTokenIndex(beforeCursorStr));
r = afterCursorStr.search(this.REGEXP_TOKEN_RIGHT);
if (r == -1) r = afterCursorStr.length;
result += afterCursorStr.substring(0, r);
return result;
} else {
return this.getValue();
}
};
var getCursorPosition = function(field) {
var pos = rf.Selection.getStart(field);
if (pos <= 0) {
// when cursorPosition is not determined (input is not focused),
// use position of last token occurence)
pos = this.getLastTokenIndex(field.value);
}
return pos;
}
var updateInputValue = function (value) {
var field = rf.getDomElement(this.fieldId);
var inputValue = field.value;
var cursorPosition = this.__getCursorPosition(field);
var beforeCursorStr = inputValue.substring(0, cursorPosition);
var afterCursorStr = inputValue.substring(cursorPosition);
var pos = this.getLastTokenIndex(beforeCursorStr);
var startPos = pos != -1 ? pos : beforeCursorStr.length;
pos = afterCursorStr.search(this.REGEXP_TOKEN_RIGHT);
var endPos = pos != -1 ? pos : afterCursorStr.length;
var beginNewValue = inputValue.substring(0, startPos) + value;
cursorPosition = beginNewValue.length;
field.value = beginNewValue + afterCursorStr.substring(endPos);
field.focus();
rf.Selection.setCaretTo(field, cursorPosition);
return field.value;
};
var getPageLastItem = function() {
if (this.items.length == 0) return -1;
var parentContainer = $(rf.getDomElement(this.id + ID.ITEMS)).parent();
var h = parentContainer.scrollTop() + parentContainer.innerHeight() + this.items[0].offsetTop;
var item;
var i = (this.index != null && this.items[this.index].offsetTop <= h) ? this.index : 0;
for (i; i < this.items.length; i++) {
item = this.items[i];
if (item.offsetTop + item.offsetHeight > h) {
i--;
break;
}
}
if (i != this.items.length - 1 && i == this.index) {
h += this.items[i].offsetTop - parentContainer.scrollTop();
for (++i; i < this.items.length; i++) {
item = this.items[i];
if (item.offsetTop + item.offsetHeight > h) {
break;
}
}
}
return i;
};
var getPageFirstItem = function() {
if (this.items.length == 0) return -1;
var parentContainer = $(rf.getDomElement(this.id + ID.ITEMS)).parent();
var h = parentContainer.scrollTop() + this.items[0].offsetTop;
var item;
var i = (this.index != null && this.items[this.index].offsetTop >= h) ? this.index - 1 : this.items.length - 1;
for (i; i >= 0; i--) {
item = this.items[i];
if (item.offsetTop < h) {
i++;
break;
}
}
if (i != 0 && i == this.index) {
h = this.items[i].offsetTop - parentContainer.innerHeight();
if (h < this.items[0].offsetTop) h = this.items[0].offsetTop;
for (--i; i >= 0; i--) {
item = this.items[i];
if (item.offsetTop < h) {
i++;
break;
}
}
}
return i;
};
/*
* Prototype definition
*/
$.extend(rf.ui.Autocomplete.prototype, (function () {
return {
/*
* public API functions
*/
name:"Autocomplete",
/*
* Protected methods
*/
__updateState: function (event) {
var subValue = this.__getSubValue();
// called from AutocompleteBase when not actually value changed
if (this.items.length == 0 && this.isFirstAjax) {
if ((this.options.ajaxMode && subValue.length >= this.options.minChars) || this.options.lazyClientMode) {
this.value = subValue;
callAjax.call(this, event, this.__show);
return true;
}
}
return false;
},
__getSubValue: getSubValue,
__getCursorPosition: getCursorPosition,
__updateInputValue: function (value) {
if (this.useTokens) {
return updateInputValue.call(this, value);
} else {
return $super.__updateInputValue.call(this, value);
}
},
__setInputValue: function (value) {
this.currentValue = this.__updateInputValue(value);
},
__onChangeValue: onChangeValue,
/*
* Override abstract protected methods
*/
__onKeyUp: function (event) {
selectItem.call(this, event, -1, true);
},
__onKeyDown: function (event) {
selectItem.call(this, event, 1, true);
},
__onPageUp: function (event) {
selectItem.call(this, event, getPageFirstItem.call(this));
},
__onPageDown: function (event) {
selectItem.call(this, event, getPageLastItem.call(this));
},
__onKeyHome: function (event) {
selectItem.call(this, event, 0);
},
__onKeyEnd: function (event) {
selectItem.call(this, event, this.items.length - 1);
},
__onBeforeShow: function (event) {
},
__onEnter: function (event) {
var value = getSelectedItemValue.call(this);
this.__onChangeValue(event, value);
this.invokeEvent("selectitem", rf.getDomElement(this.id), event, value);
},
__onShow: function (event) {
if (this.options.selectFirst) {
selectItem.call(this, event, 0);
}
},
__onHide: function (event) {
selectItem.call(this, event);
},
/*
* Destructor
*/
destroy: function () {
//TODO: add all unbind
this.items = null;
this.cache = null;
var itemsContainer = rf.getDomElement(this.id + ID.ITEMS);
$(itemsContainer).removeData();
rf.Event.unbind(itemsContainer.parentNode, this.namespace);
this.__conceal();
$super.destroy.call(this);
}
};
})());
$.extend(rf.ui.Autocomplete, {
setData: function (id, data) {
$(rf.getDomElement(id)).data("componentData", data);
},
__getLastTokenIndex: function (tokens, value) {
var LAST_TOKEN_OCCURENCE = new RegExp("[" + tokens + "][^" + tokens + "]*$", "i");
var AFTER_LAST_TOKEN_WITH_SPACES = new RegExp("[^" + tokens + " ]", "i");
var value = value || "";
var lastTokenIndex = value.search(LAST_TOKEN_OCCURENCE);
if (lastTokenIndex < 0) {
return 0;
}
var beforeToken = value.substring(lastTokenIndex);
var afterLastTokenIndex = beforeToken.search(AFTER_LAST_TOKEN_WITH_SPACES);
if (afterLastTokenIndex <= 0) {
afterLastTokenIndex = beforeToken.length;
}
return lastTokenIndex + afterLastTokenIndex;
}
});
})(RichFaces.jQuery, RichFaces);