uxrocket.autocomplete
Version:
UX Rocket Autocomplete: A jQueryUI autocomplete wrapper
523 lines (424 loc) • 16.5 kB
JavaScript
/**
* UX Rocket
* jQuery based accordion
* @author Bilal Cinarli
* @dependency jQueryUI Autocomplete
* @dependency Handlebars
* @dependency UX Rocket Clear
*/
;
(function($) {
var ux, // local shorthand
defaults = {
type : "list",
item : 10,
service : null,
minLength : 2,
formdata : null,
categoryTextLimit: 25,
queryType : 'GET',
hidden : null,
template : null,
highlight : true,
arrowSelection : false,
arrowNavigation : true,
cache : false,
// callbacks
onReady : false,
onSelect : false,
onButtonClick : false,
onClearCache : false,
onUpdate : false,
onRemove : false
},
templates = {
list : {
wrap: '<ul></ul>',
item: '' +
'<li>' +
' <a {{#if url}}href="{{url}}"{{/if}}>' +
' {{{name}}}' +
' {{#if title}}' +
' <br /><em>{{substr title 50}}</em>' +
' {{/if}}' +
' </a>' +
'</li>'
},
image : {
wrap: '<ul></ul>',
item: '' +
'<li>' +
' <a {{#if url}}href="{{url}}"{{/if}}>' +
' <span class="item-image"><img src="{{image}}"/></span> ' +
' {{{name}}}' +
' {{#if title}}' +
' <br /><em>{{substr title 50}}</em>' +
' {{/if}}' +
' </a>' +
'</li>'
},
category: {
wrap: '<ul></ul>',
head: '' +
'<li class="uxitd-autocomplete-category ui-autocomplete-category">' +
' <span class="uxitd-category-head" title="{{category}}">{{substr category 25}}</span>' +
'</li>',
item: '' +
'<li>' +
' <a {{#if url}}href="{{url}}"{{/if}}>' +
' {{{name}}}' +
' {{#if title}}' +
' <br /><em>{{substr title 50}}</em>' +
' {{/if}}' +
' </a>' +
'</li>'
},
tree : {
wrap: '<ul></ul>',
head: '' +
'<li class="uxitd-autocomplete-category ui-autocomplete-category">' +
' <span class="uxitd-category-head" title="{{category}}">{{substr category 25}}</span>' +
'</li>',
item: '' +
'<li>' +
' <a {{#if url}}href="{{url}}"{{/if}}>' +
' <span class="item-image"><img src="{{image}}"/></span> ' +
' {{{name}}}' +
' {{#if title}}' +
' <br /><em>{{substr title 50}}</em>' +
' {{/if}}' +
' </a>' +
'</li>'
}
},
events = {
click : 'click.uxAutocomplete',
search : 'autocompletesearch.uxAutocomplete',
response: 'autocompleteresponse.uxAutocomplete'
},
ns = {
rocket : 'uxRocket',
data : 'uxAutocomplete',
ready : 'uxitd-autocomplete-ready',
rocketWrap: 'uxitd-plugin-wrap',
wrap : 'uxitd-autocomplete-wrap',
icon : 'icon-search',
loading : 'icon-loading'
};
var AutoComplete = function(el, options, selector) {
var $el = $(el),
opts = $.extend({}, defaults, options, $el.data(), {'selector': selector});
// terms object for caching
opts.terms = {};
$el.data(ns.data, opts);
// set element layout
setLayout($el);
// call Ready Callback
callback(opts.onReady);
// bind actions
bindUIActions($el);
// set template actions
setTemplate($el);
};
var setLayout = function($el) {
var columns,
_opts = $el.data(ns.data);
columns = ' ' + $el.context.className.replace(ns.ready, '');
if(_opts.selector.charAt(0) == '.') {
columns = columns.replace(' ' + _opts.selector.substr(1), '');
columns = columns.replace(_opts.selector.substr(1) + ' ', '');
}
if($el.parent().is('.' + ns.rocketWrap)) {
$el.parent().addClass(ns.wrap + columns + ' group');
}
else {
$el.wrap('<span class="' + ns.rocketWrap + ' ' + ns.wrap + columns + ' group"></span>');
}
$el.after('<i class="' + ns.icon + '"></i>');
};
var setSource = function($el) {
var _opts = $el.data(ns.data),
fdata = null,
url_pattern = /^(http|https|\/|\.\/|\.\.\/)/,
source = _opts.service,
sourceFunction;
if(!url_pattern.test(source)) {
source = eval(source);
sourceFunction = function(request, response) {
// check if element is active or not : fix for IE
if(!$el.is(':focus')) {
return;
}
var matcher = new RegExp($.ui.autocomplete.escapeRegex(request.term), "i");
response($.map(source.itemList, function(item) {
if(matcher.test(item.name)) {
return setItem(item);
}
}));
};
}
else {
sourceFunction = function(request, response) {
var term = request.term;
// check if element is active or not : fix for IE
if(!$el.is(':focus')) {
return;
}
if(_opts.cache && term in _opts.terms) {
response($.map(_opts.terms[term].itemList.slice(0, _opts.item), function(item) {
return setItem(item);
}));
return;
}
if(_opts.formdata != null) {
fdata = $(_opts.formdata).serialize();
}
$.ajax({
url : source,
dataType: 'json',
type : _opts.queryType,
data : {
type : _opts.type,
term : term,
formdata: fdata
},
success : function(data) {
if(_opts.cache) {
_opts.terms[term] = data;
$el.data(ns.data, _opts);
}
response($.map(data.itemList.slice(0, _opts.item), function(item) {
return setItem(item);
}));
}
});
};
}
return sourceFunction;
};
var bindUIActions = function($el) {
var _opts = $el.data(ns.data);
if(typeof _opts.props !== 'undefined' && _opts.props != '') {
var props = _opts.props.split(':');
$el.next('.' + ns.icon).addClass('uxitd-autocomplete-icon-clickable').on(events.click, function(e) {
e.preventDefault();
eval(props[1]);
});
}
if(_opts.onButtonClick !== false) {
$el.next('.' + ns.icon).on(events.click, function() {
callback(_opts.onButtonClick);
});
}
$el.on(events.search, function(event, ui) {
$(this).siblings('.' + ns.icon).addClass(ns.loading);
});
$el.on(events.response, function(event, ui) {
$(this).siblings('.' + ns.loading).removeClass(ns.loading);
});
$el.autocomplete({
open : function(event, ui) {
$(".uxitd-autocomplete-category").removeClass('ui-menu-item');
$(this).autocomplete("widget").css({
"width": ($(this).outerWidth() + "px")
});
},
minLength: _opts.minLength,
source : setSource($el),
focus : function(event, ui) {
if(!_opts.arrowNavigation) {
event.preventDefault();
}
},
select : function(event, ui) {
if(_opts.hidden != null) {
$(_opts.hidden).val(ui.item.id);
}
if(_opts.arrowSelection === true && event.keyCode == 13 && typeof ui.item.url !== 'undefined') {
// check if target is blank or not
var _target = $("#" + event.currentTarget.id).find(".ui-state-focus a").attr("target");
if(_target == '_blank') {
window.open(ui.item.url);
}
else {
window.location = ui.item.url;
}
}
callback(_opts.onSelect);
}
});
};
var setTemplate = function($el) {
var autocomplete = $el.data("uiAutocomplete") ? $el.data("uiAutocomplete") : $el.data("ui-autocomplete"),
_opts = $el.data(ns.data),
template = {};
if(_opts.template === null) {
switch(_opts.type) {
default:
case 'list':
template = templates.list;
break;
case 'image':
template = templates.image;
break;
case 'category':
template = templates.category;
break;
case 'tree':
template = templates.tree;
break;
}
}
else {
template = _opts.template;
}
autocomplete._renderMenu = function(ul, items) {
var _this = this,
_list = [],
currentCategory = "";
$.each(items, function(index, item) {
var temp;
if((_opts.type == 'category' || _opts.type == 'tree') && typeof item.category !== 'undefined' && item.category != currentCategory) {
var categoryHead = Handlebars.compile(template.head)(item);
_list.push(categoryHead);
currentCategory = item.category;
}
temp = _this._renderItemData(ul, item);
_list.push(temp);
});
ul.append(_list);
if(currentCategory != "") {
ul.addClass('uxitd-category');
}
};
autocomplete._create = function() {
var _this = this;
_this._super();
_this.widget().menu("option", "items", "> :not(.ui-autocomplete-category)");
};
autocomplete._renderItemData = function(ul, item) {
var rendered,
listItem = Handlebars.compile(template.item)(item);
rendered = listItem;
if(_opts.highlight === true) {
rendered = listItem.replace(
new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + $.ui.autocomplete.escapeRegex(this.term) + ")(?![^<>]*>)(?![^&;]+;)", "gi"),
'<span class="matched">$1</span>');
}
return $(rendered)
.addClass('ui-menu-item')
.data("ui-autocomplete-item", item)
.attr('role', 'presentation');
};
};
// fallback for 0.x.x versions
var setItem = function(item) {
if(typeof item.value === 'undefined') {
item.value = item.name;
}
/**
* Fallback for old data json format
* @deprecated visiblename
*/
if(typeof item.visiblename !== 'undefined') {
item.value = item.visiblename;
}
return item;
};
// global callback
var callback = function(fn) {
// if callback string is function call it directly
if(typeof fn === 'function') {
fn.apply(this);
}
// if callback defined via data-attribute, call it via new Function
else {
if(fn !== false) {
var func = new Function('return ' + fn);
func();
}
}
};
// jquery bindings
ux = $.fn.uxitdautocomplete = $.uxautocomplete = function(options) {
var selector = this.selector;
return this.each(function() {
var $el = $(this),
uxrocket = $el.data('uxRocket') || {},
autocomplete;
if($el.hasClass(ns.ready) || $el.hasClass(ns.rocketWrap)) {
return;
}
$el.addClass(ns.ready);
uxrocket[ns.data] = {'hasWrapper': true, 'wrapper': ns.wrap, 'ready': ns.ready, 'selector': selector, 'options': options};
$el.data(ns.rocket, uxrocket);
autocomplete = new AutoComplete(this, options, selector);
});
};
ux.clearCache = function(el) {
var $el = el !== undefined ? $(el) : $("." + ns.ready);
$el.filter('input').each(function() {
var _this = $(this),
_instance = _this.data(ns.data);
// set cache terms to empty object
_instance.terms = {};
_this.data(ns.data, _instance);
callback(_instance.onClearCache);
});
};
ux.update = function(el) {
var $el = el !== undefined ? $(el) : $("." + ns.ready);
$el.filter('input').each(function() {
var _this = $(this),
_instance = _this.data(ns.data),
_rocket = _this.data(ns.rocket);
// first remove jQuery UI Autocomplete
_this.autocomplete('destroy');
// remove previous instance data
// since we are removing the rocket data no need to clear cache separately
_this.removeData(ns.data);
// add new options
_instance = $.extend({}, defaults, _rocket[ns.data]['options'], $(this).data());
// register new instance data
_this.data(ns.data, _instance);
_this.off(events.click + ' ' + events.search + ' ' + events.response);
bindUIActions(_this);
setTemplate(_this);
callback(_instance.onUpdate);
});
};
ux.remove = function(el) {
var $el = el !== undefined ? $(el) : $("." + ns.ready);
$el.filter('input').each(function() {
var _this = $(this),
_instance = _this.data(ns.data),
_uxrocket = _this.data(ns.rocket);
// remove ready class
_this.removeClass(ns.ready);
// remove plugin events
_this.off(events.click + ' ' + events.search + ' ' + events.response);
// remove icon and wrapper
_this.next('.' + ns.icon).remove();
if(_uxrocket[ns.data].hasWrapper) {
if(Object.keys && Object.keys(_uxrocket).length == 1) {
_this.unwrap();
}
else {
_this.parent().removeClass(ns.wrap);
}
}
// remove plugin data
_this.removeData(ns.data);
// remove uxRocket registry
delete _uxrocket[ns.data];
_this.data(ns.rocket, _uxrocket);
// remove jQueryUI
_this.autocomplete('destroy');
callback(_instance.onRemove);
});
};
// version
ux.version = '1.6.0';
// settings
ux.settings = defaults;
})(jQuery);