Spade
Mini Shell
| Directory:~$ /home/lmsyaran/public_html/joomla5/media/com_fabrik/js/ |
| [Home] [System Details] [Kill Me] |
/**
* Bootstrap Auto-Complete
*
* @copyright: Copyright (C) 2005-2016 Media A-Team, Inc. - All rights
reserved.
* @license: GNU/GPL http://www.gnu.org/copyleft/gpl.html
*/
/*jshint mootools: true */
/*global fconsole:true, Joomla:true, */
define(['jquery', 'fab/encoder',
'fab/fabrik',
'lib/debounce/jquery.ba-throttle-debounce'],
function (jQuery, Encoder, Fabrik, debounce) {
var AutoComplete = new Class({
Implements: [Options, Events],
Binds: [],
options: {
menuclass : 'auto-complete-container
dropdown',
classes : {
'ul': 'dropdown-menu',
'li': 'result'
},
url : 'index.php',
max : 10,
onSelection : Class.empty,
autoLoadSingleResult : true,
minTriggerChars : 1,
debounceDelay : 500,
storeMatchedResultsOnly: false // Only store a value if
selected from picklist
},
initialize: function (element, options) {
// not sure why we use domready, but causes issues in popups on
second+ open, doesn't fire
//window.addEvent('domready', function () {
this.matchedResult = false;
this.setOptions(options);
element = element.replace('-auto-complete',
'');
this.options.labelelement = typeOf(document.id(element +
'-auto-complete')) === 'null' ?
document.getElement(element +
'-auto-complete') : document.id(element +
'-auto-complete');
this.cache = {};
this.selected = -1;
this.mouseinsde = false;
document.addEvent('keydown', function (e) {
this.doWatchKeys(e);
}.bind(this));
this.element = typeOf(document.id(element)) ===
'null' ?
document.getElement(element) : document.id(element);
this.buildMenu();
if (!this.getInputElement()) {
fconsole('autocomplete didn\'t find input
element');
return;
}
this.getInputElement().setProperty('autocomplete',
'off');
/*
this.getInputElement().addEvent('keyup',
function (e) {
this.search(e);
}.bind(this));
*/
/**
* Using a 3rd party jQuery lib to 'debounce' the
input, so the search doesn't fire until
* the user has stopped typing for more than X ms.
Don't use on() here, use bind(), otherwise
* we get multiple events when form popups are opened
multiple times.
*/
var self = this;
jQuery(this.getInputElement()).bind('keyup',
debounce(this.options.debounceDelay, function (e) {
self.search(e);
//console.log('heyup');
}));
jQuery(this.getInputElement()).bind('input',
debounce(this.options.debounceDelay, function (e) {
self.search(e);
//console.log('input');
}));
this.getInputElement().addEvent('blur', function
(e) {
if (this.options.storeMatchedResultsOnly) {
if (!this.matchedResult) {
if (typeof(this.data) === 'undefined'
||
!(this.data.length === 1 &&
this.options.autoLoadSingleResult)) {
this.element.value = '';
}
}
}
}.bind(this));
//}.bind(this));
},
search: function (e) {
var msg;
/**
* NOTE that because we use a jQuery event to trigger this, e
is a jQuery event, so keyCode
* instead of code, and e.preventDefault() instead of e.stop()
*/
if (!this.isMinTriggerlength()) {
return;
}
if (e.which === 9 || e.which === 13) {
e.preventDefault();
this.closeMenu();
if (this.ajax) {
this.ajax.cancel();
}
this.element.fireEvent('change', new
Event.Mock(this.element, 'change'), 500);
return;
}
this.matchedResult = false;
var v = this.getInputElement().get('value');
if (v === '') {
this.element.value = '';
}
if (v !== this.searchText && v !== '') {
if (this.options.storeMatchedResultsOnly === false) {
this.element.value = v;
}
this.positionMenu();
if (this.cache[v]) {
if (this.populateMenu(this.cache[v])) {
this.openMenu();
}
} else {
if (this.ajax) {
this.closeMenu();
this.ajax.cancel();
}
this.ajax = new Request({
url : this.options.url,
data : {
value: v
},
onRequest: function () {
Fabrik.loader.start(this.getInputElement());
}.bind(this),
onCancel : function () {
Fabrik.loader.stop(this.getInputElement());
this.ajax = null;
}.bind(this),
onSuccess: function (e) {
Fabrik.loader.stop(this.getInputElement());
this.ajax = null;
if (typeOf(e) === 'null') {
fconsole('Fabrik autocomplete: Ajax
response empty');
var elModel =
Fabrik.getBlock(this.options.formRef).formElements.get(this.element.id);
msg =
Joomla.JText._('COM_FABRIK_AUTOCOMPLETE_AJAX_ERROR');
elModel.setErrorMessage(msg,
'fabrikError', true);
return;
}
this.completeAjax(e, v);
}.bind(this),
onFailure: function (xhr) {
Fabrik.loader.stop(this.getInputElement());
this.ajax = null;
fconsole('Fabrik autocomplete: Ajax
failure: Code ' + xhr.status + ': ' + xhr.statusText);
var elModel =
Fabrik.getBlock(this.options.formRef).formElements.get(this.element.id);
msg =
Joomla.JText._('COM_FABRIK_AUTOCOMPLETE_AJAX_ERROR');
elModel.setErrorMessage(msg,
'fabrikError', true);
}.bind(this)
}).send();
}
}
else {
//console.log('same same or empty');
}
this.searchText = v;
},
completeAjax: function (r, v) {
r = JSON.parse(r);
this.cache[v] = r;
if (this.populateMenu(r)) {
this.openMenu();
}
},
buildMenu: function () {
this.menu = new Element('ul.dropdown-menu',
{'role': 'menu', 'styles':
{'z-index': 1056}});
this.menu.inject(document.body);
this.menu.addEvent('mouseenter', function () {
this.mouseinsde = true;
}.bind(this));
this.menu.addEvent('mouseleave', function () {
this.mouseinsde = false;
}.bind(this));
this.menu.addEvent('click:relay(a)', function (e,
target) {
this.makeSelection(e, target);
}.bind(this));
},
getInputElement: function () {
return this.options.labelelement ? this.options.labelelement :
this.element;
},
positionMenu: function () {
var coords = this.getInputElement().getCoordinates();
// var pos = this.getInputElement().getPosition();
this.menu.setStyles({'left': coords.left,
'top': (coords.top + coords.height) - 1, 'width':
coords.width});
},
populateMenu: function (data) {
// $$$ hugh - added decoding of things like & in the
text strings
var li, a, form, elModel, blurEvent, pair;
data.map(function (item, index) {
item.text = Encoder.htmlDecode(item.text);
return item;
});
this.data = data;
var max = this.getListMax(),
ul = this.menu;
ul.empty();
if (data.length === 1 &&
this.options.autoLoadSingleResult) {
this.element.value = data[0].value;
this.getInputElement().value = data[0].text;
// $$$ Paul - The selection event is for text being
selected in an input field not for a
// link being selected
this.closeMenu();
this.fireEvent('selection', [this,
this.element.value]);
// $$$ hugh - need to fire change event, in case it's
something like a join element
// with a CDD that watches it.
form = Fabrik.getBlock(this.options.formRef);
if (form !== false) {
elModel = form.formElements.get(this.element.id);
blurEvent = elModel.getBlurEvent();
this.element.fireEvent(blurEvent, new
Event.Mock(this.element, blurEvent), 700);
}
// $$$ hugh - fire a Fabrik event, just for good luck. :)
Fabrik.fireEvent('fabrik.autocomplete.selected',
[this, this.element.value]);
return false;
}
if (data.length === 0) {
li = new Element('li').adopt(new
Element('div.alert.alert-info')
.adopt(new Element('i').set('text',
Joomla.JText._('COM_FABRIK_NO_AUTOCOMPLETE_RECORDS'))));
li.inject(ul);
}
for (var i = 0; i < max; i++) {
pair = data[i];
a = new Element('a', {'href':
'#', 'data-value': pair.value, tabindex:
'-1'}).set('text', pair.text);
li = new Element('li').adopt(a);
li.inject(ul);
}
if (data.length > this.options.max) {
new Element('li').set('text',
'....').inject(ul);
}
return true;
},
makeSelection: function (e, li) {
e.preventDefault();
// $$$ tom - make sure an item was selected before operating on
it.
if (typeOf(li) !== 'null') {
this.getInputElement().value = li.get('text');
this.element.value =
li.getProperty('data-value');
this.closeMenu();
this.fireEvent('selection', [this,
this.element.value]);
// $$$ hugh - need to fire change event, in case it's
something like a join element
// with a CDD that watches it.
this.element.fireEvent('change', new
Event.Mock(this.element, 'change'), 700);
this.element.fireEvent('blur', new
Event.Mock(this.element, 'blur'), 700);
// $$$ hugh - fire a Fabrik event, just for good luck. :)
Fabrik.fireEvent('fabrik.autocomplete.selected',
[this, this.element.value]);
} else {
/**
* $$$ Paul - The Fabrik event below makes NO sense.
* This is a code error condition not an event because
typeOf(li) should never be null
**/
// $$$ tom - fire a notselected event to let developer
take appropriate actions.
Fabrik.fireEvent('fabrik.autocomplete.notselected', [this,
this.element.value]);
}
},
closeMenu: function () {
if (this.shown) {
this.shown = false;
// some templates seem to need a jQuery hide, something to
do with webkit
jQuery(this.menu).hide();
this.selected = -1;
document.removeEvent('click', this.doCloseEvent);
}
},
openMenu: function () {
if (!this.shown) {
if (this.isMinTriggerlength()) {
this.menu.show();
this.shown = true;
this.doCloseEvent = this.doTestMenuClose.bind(this);
document.addEvent('click',
this.doCloseEvent);
this.selected = 0;
this.highlight();
}
}
},
isMinTriggerlength: function () {
var v = this.getInputElement().get('value');
return v.length >= this.options.minTriggerChars;
},
doTestMenuClose: function () {
if (!this.mouseinsde) {
this.closeMenu();
}
},
getListMax: function () {
if (typeOf(this.data) === 'null') {
return 0;
}
return this.data.length > this.options.max ?
this.options.max : this.data.length;
},
doWatchKeys: function (e) {
if (document.activeElement !== this.getInputElement()) {
return;
}
var max = this.getListMax(), selected, selectEvnt;
if (!this.shown) {
// Stop enter from submitting when in in-line edit form.
if (e.code.toInt() === 13) {
e.stop();
}
if (e.code.toInt() === 40) {
this.openMenu();
}
} else {
if (!this.isMinTriggerlength()) {
e.stop();
this.closeMenu();
}
else {
if (e.key === 'enter' || e.key ===
'tab') {
window.fireEvent('blur');
}
switch (e.code) {
case 40://down
if (!this.shown) {
this.openMenu();
}
if (this.selected + 1 <= max) {
this.selected++;
}
this.highlight();
e.stop();
break;
case 38: //up
if (this.selected - 1 >= -1) {
this.selected--;
this.highlight();
}
e.stop();
break;
case 13://enter
case 9://tab
e.stop();
selected = this.getSelected();
if (selected) {
selectEvnt = new Event.Mock(selected,
'click');
this.makeSelection(selectEvnt, selected);
this.closeMenu();
}
break;
case 27://escape
e.stop();
this.closeMenu();
break;
}
}
}
},
/**
* Get the selected <a> tag
*
* @return DOM Node <a>
*/
getSelected: function () {
var all = this.menu.getElements('li'),
lis = all.filter(function (li, i) {
return i === this.selected;
}.bind(this));
if (typeOf(lis[0]) === 'element') {
return lis[0].getElement('a');
} else if (all.length > 0) {
// Can occur if autocomplete generated but not clicked on /
keyed into.
return all[0].getElement('a');
}
return false;
},
highlight: function () {
this.matchedResult = true;
this.menu.getElements('li').each(function (li, i) {
if (i === this.selected) {
li.addClass('selected').addClass('active');
} else {
li.removeClass('selected').removeClass('active');
}
}.bind(this));
}
});
return AutoComplete;
});