'use strict';
var jQuery = require('jquery');
var InvalidControlModelError = require('br/presenter/control/InvalidControlModelError');
var AutoCompleteSelectionField = require('br/presenter/node/AutoCompleteSelectionField');
var PropertyListener = require('br/presenter/property/PropertyListener');
var ControlAdaptor = require('br/presenter/control/ControlAdaptor');
var Core = require('br/Core');
/**
* @module br/presenter/control/selectionfield/JQueryAutoCompleteControl
*/
/**
* @class
* @alias module:br/presenter/control/selectionfield/JQueryAutoCompleteControl
* @extends module:br/presenter/control/ControlAdaptor
* @extends module:br/presenter/property/PropertyListener
*
* @classdesc
* Provides an input box that supports auto complete when used in conjunction with a
* {@link module:br/presenter/node/AutoCompleteSelectionField}.
*
* <p>The jQuery auto complete control is aliased by <em>br.autocomplete-box</em>, and can
* be used within templates as follows:</p>
*
* <pre>
* <span data-bind="controlNode:autoCompleteSelectionFieldProperty, control:'br.autocomplete-box'"></span>
* </pre>
*
* <p>You can also use an exsisting text input element and avoid having an extra container element around the control:</p>
*
* <pre>
* <input type="text" data-bind="controlNode:autoCompleteSelectionFieldProperty, control:'br.autocomplete-box'"/>
* </pre>
*
* <p>
* Options: <em>openOnFocus</em> - Show the auto complete selection on input focus (true|false). Defaults to false.
* <em>appendTo</em> - Specify the jquery selector of the element that the menu should be appended to.
* <em>minCharAmount</em> - Specify the minimun amount of characters to be typed before the autocomplete menu is displayed. Default is 0.
* </p>
*
* @see br.presenter.node.AutoCompleteSelectionField
*/
function JQueryAutoCompleteControl() {
ControlAdaptor.call(this);
/** @private */
this.m_eElement = {};
this.m_jQueryInput = null;
this.m_bOpenOnFocus = false;
this.m_sAppendTo = 'body';
this._viewOpened = false;
}
Core.inherit(JQueryAutoCompleteControl, ControlAdaptor);
Core.inherit(JQueryAutoCompleteControl, PropertyListener);
/**
* @private
* @see br.presenter.control.ControlAdaptor#setElement
*/
JQueryAutoCompleteControl.prototype.setElement = function(eElement) {
if (eElement.type && eElement.type === 'text') {
this.m_eElement = eElement;
} else {
this.m_eElement = document.createElement('input');
eElement.appendChild(this.m_eElement);
}
};
/**
* @private
* @see br.presenter.control.ControlAdaptor#setPresentationNode
*/
JQueryAutoCompleteControl.prototype.setPresentationNode = function(oPresentationNode) {
if (!(oPresentationNode instanceof AutoCompleteSelectionField)) {
throw new InvalidControlModelError('JQueryAutoCompleteControl', 'AutoCompleteSelectionField');
}
this.m_eElement.value = oPresentationNode.value.getFormattedValue();
this._valueChangedListener = oPresentationNode.value.addUpdateListener(this, '_valueChanged');
this.m_oPresentationNode = oPresentationNode;
};
JQueryAutoCompleteControl.prototype.onViewReady = function() {
this.m_jQueryInput = jQuery(this.m_eElement);
var self = this;
this.m_jQueryInput.autocomplete({
delay: this.delay || 0,
minLength: self.m_nMinCharAmount || 0,
autoFocus: true,
appendTo: self.m_sAppendTo,
open: function() {
// ensure menu is on top of elements
self.m_jQueryInput.autocomplete('widget').css('z-index', 999999);
self.m_jQueryInput.addClass('autocomplete-menu-open');
return false;
},
close: function() {
self.m_jQueryInput.removeClass('autocomplete-menu-open');
},
source: function(request, response) {
var sTerm = request.term;
self.m_oPresentationNode.getAutoCompleteList(sTerm, function(pValues) {
response(pValues);
});
},
select: function(event, oUi) {
self._setValue(self.m_oPresentationNode, this, oUi);
// if the selection is triggered by a click, not by pressing enter, then blur
if (self.m_bBlurAfterClick === true) {
var ie8LeftClick = !event.button && !event.which;
if (event.which === 1 || ie8LeftClick) {
this.blur();
}
}
return false;
}
});
this._onDocumentFocus = this._onDocumentFocus.bind(this);
this.m_jQueryInput.on('focus', this._onDocumentFocus);
this._onScroll = this._onScroll.bind(this);
jQuery( document.body ).on('mousewheel wheel', this._onScroll);
this._viewOpened = true;
};
JQueryAutoCompleteControl.prototype._onScroll = function(wheelEvent) {
var isEventTargetChildOfAutoComplete = this.m_jQueryInput.autocomplete('widget')[0].contains(wheelEvent.target);
if( isEventTargetChildOfAutoComplete === false ) {
this.m_jQueryInput.autocomplete('close');
}
};
JQueryAutoCompleteControl.prototype._onDocumentFocus = function() {
this.m_jQueryInput.select();
if (this.m_bOpenOnFocus) {
this.m_jQueryInput.autocomplete('search', this.m_jQueryInput.val() || '');
}
};
JQueryAutoCompleteControl.prototype._setValue = function(oPresentationNode, oInput, oUi) {
var isValidOption = oPresentationNode.isValidOption( oInput.value );
if ( isValidOption || oUi ) {
var presentationNodeValue = isValidOption ? oInput.value : oUi.item.value;
oPresentationNode.value.setValue( presentationNodeValue );
this.m_eElement.value = ( this.clearTextAfterValidInput ? '' : oPresentationNode.value.getFormattedValue());
}
};
JQueryAutoCompleteControl.prototype._valueChanged = function() {
this.m_eElement.value = this.m_oPresentationNode.value.getFormattedValue();
};
/**
* @private
*/
JQueryAutoCompleteControl.prototype.setOptions = function(mOptions) {
mOptions = mOptions || {};
if (mOptions.openOnFocus !== undefined && mOptions.openOnFocus !== 'false') {
this.m_bOpenOnFocus = true;
}
if (mOptions.appendTo !== undefined) {
this.m_sAppendTo = mOptions.appendTo;
}
if (mOptions.minCharAmount !== undefined) {
this.m_nMinCharAmount = mOptions.minCharAmount;
}
if (mOptions.blurAfterClick !== undefined) {
this.m_bBlurAfterClick = mOptions.blurAfterClick;
}
if (mOptions.delay !== undefined) {
this.delay = mOptions.delay;
}
if (mOptions.clearTextAfterValidInput !== undefined) {
this.clearTextAfterValidInput = mOptions.clearTextAfterValidInput;
}
};
/**
* Destroy created listeners and jQuery autocomplete plugin
*/
JQueryAutoCompleteControl.prototype.destroy = function() {
// if onOpen is never called the control wouldn't be initialised, hence we must guard against that
if(this._viewOpened) {
this.m_jQueryInput.off('focus', this._onDocumentFocus);
jQuery( document.body ).off('mousewheel wheel', this._onScroll);
this.m_jQueryInput.autocomplete('destroy');
this.m_jQueryInput.off();
}
if (this._valueChangedListener) {
this.m_oPresentationNode.value.removeListener(this._valueChangedListener);
}
this._valueChangedListener = null;
this.m_jQueryInput = null;
this.m_eElement = null;
};
module.exports = JQueryAutoCompleteControl;