|
|
- (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);
|