wmdeit_kaform/addons/autocomplete/kube.autocomplete.js

285 lines
7.0 KiB
JavaScript

(function($K)
{
$K.add('module', 'autocomplete', {
init: function(app, context)
{
this.app = app;
this.$doc = app.$doc;
this.$win = app.$win;
this.$body = app.$body;
this.animate = app.animate;
// defaults
var defaults = {
url: false,
min: 2,
labelClass: false,
target: false,
param: false
};
// context
this.context = context;
this.params = context.getParams(defaults);
this.$element = context.getElement();
this.$target = context.getTarget();
},
start: function()
{
this._build();
this.timeout = null;
this.$element.on('keyup.kube.autocomplete', this._open.bind(this));
},
stop: function()
{
this.$box.remove();
this.$element.off('.kube.autocomplete');
this.$doc.off('.kube.autocomplete');
this.$win.off('.kube.autocomplete');
},
// private
_build: function()
{
this.$box = $K.dom('<div />');
this.$box.addClass('autocomplete');
this.$box.addClass('is-hidden');
this.$body.append(this.$box);
if (this.$target && !this._isInputTarget())
{
this.$target.addClass('autocomplete-labels');
var $closes = this.$target.find('.close');
$closes.on('click', this._removeLabel.bind(this));
}
},
_open: function(e)
{
if (e) e.preventDefault();
clearTimeout(this.timeout);
var value = this.$element.val();
if (value.length >= this.params.min)
{
this._resize();
this.$win.on('resize.kube.autocomplete', this._resize.bind(this));
this.$doc.on('click.kube.autocomplete', this._close.bind(this));
this.$box.addClass('is-open');
this._listen(e);
}
else
{
this._close(e);
}
},
_close: function(e)
{
if (e) e.preventDefault();
this.$box.removeClass('is-open');
this.$box.addClass('is-hidden');
this.$doc.off('.kube.autocomplete');
this.$win.off('.kube.autocomplete');
},
_getPlacement: function(pos, height)
{
return ((this.$doc.height() - (pos.top + height)) < this.$box.height()) ? 'top' : 'bottom';
},
_resize: function()
{
this.$box.width(this.$element.width());
},
_getParamName: function()
{
return (this.params.param) ? this.params.param : this.$element.attr('name');
},
_getTargetName: function()
{
var name = this.$target.attr('data-name');
return (name) ? name : this.$target.attr('id');
},
_lookup: function()
{
var data = this._getParamName() + '=' + this.$element.val();
$K.ajax.post({
url: this.params.url,
data: data,
success: this._complete.bind(this)
});
},
_complete: function(json)
{
this.$box.html('');
if (json.length === 0) return this._close();
for (var i = 0; i < json.length; i++)
{
var $item = $K.dom('<a>');
$item.attr('href', '#');
$item.attr('rel', json[i].id);
$item.html(json[i].name);
$item.on('click', this._set.bind(this));
this.$box.append($item);
}
var pos = this.$element.offset();
var height = this.$element.height();
var width = this.$element.width();
var placement = this._getPlacement(pos, height);
var top = (placement === 'top') ? (pos.top - this.$box.height() - height) : (pos.top + height);
this.$box.css({ width: width + 'px', top: top + 'px', left: pos.left + 'px' });
this.$box.removeClass('is-hidden');
},
_listen: function(e)
{
switch(e.which)
{
case 40: // down
e.preventDefault();
this._select('next');
break;
case 38: // up
e.preventDefault();
this._select('prev');
break;
case 13: // enter
e.preventDefault();
this._set();
break;
case 27: // esc
this._close(e);
break;
default:
this.timeout = setTimeout(this._lookup.bind(this), 300);
break;
}
},
_select: function(type)
{
var $links = this.$box.find('a');
var $active = this.$box.find('.is-active');
$links.removeClass('is-active');
var $item = this._selectItem($active, $links, type);
$item.addClass('is-active');
},
_selectItem: function($active, $links, type)
{
var $item;
var isActive = ($active.length !== 0);
var size = (type === 'next') ? 0 : ($links.length - 1);
if (isActive)
{
$item = $active[type]();
}
if (!isActive || !$item || $item.length === 0)
{
$item = $links.eq(size);
}
return $item;
},
_set: function(e)
{
var $active = this.$box.find('.is-active');
if (e)
{
e.preventDefault();
$active = $K.dom(e.target);
}
var id = $active.attr('rel');
var value = $active.html();
if (this.$target.length !== 0)
{
if (this._isInputTarget())
{
this.$target.val(value);
}
else
{
var $added = this.$target.find('[data-id="' + id + '"]');
if ($added.length === 0)
{
this._addLabel(id, value);
}
}
this.$element.val('');
}
else
{
this.$element.val(value);
}
this.$element.focus();
this.app.broadcast('autocomplete.set', this, value);
this._close();
},
_addLabel: function(id, name)
{
var $label = $K.dom('<span>');
$label.addClass('label');
$label.attr('data-id', id);
$label.text(name + ' ');
if (this.params.labelClass)
{
$label.addClass(this.params.labelClass);
}
var $close = $K.dom('<span>');
$close.addClass('close');
$close.on('click', this._removeLabel.bind(this));
var $input = $K.dom('<input>');
$input.attr('type', 'hidden');
$input.attr('name', this._getTargetName() + '[]');
$input.val(name);
$label.append($close);
$label.append($input);
this.$target.append($label);
},
_isInputTarget: function()
{
return (this.$target.get().tagName === 'INPUT');
},
_removeLabel: function(e)
{
e.preventDefault();
var $el = $K.dom(e.target);
var $label = $el.closest('.label');
this.animate.run($label, 'fadeOut', function()
{
$label.remove();
}.bind(this))
}
});
})(Kube);