1 (function ($, rf) {
  2 
  3     rf.ui = rf.ui || {};
  4 
  5     // Constructor definition
  6     rf.ui.AutocompleteBase = function(componentId, selectId, fieldId, options) {
  7         // call constructor of parent class
  8         $super.constructor.call(this, componentId);
  9         this.selectId = selectId;
 10         this.fieldId = fieldId;
 11         this.options = $.extend({}, defaultOptions, options);
 12         this.namespace = this.namespace || "." + rf.Event.createNamespace(this.name, this.selectId);
 13         this.currentValue = $(rf.getDomElement(fieldId)).val();
 14         this.tempValue = this.getValue();
 15         this.isChanged = this.tempValue.length != 0;
 16         bindEventHandlers.call(this);
 17     };
 18 
 19     // Extend component class and add protected methods from parent class to our container
 20     rf.BaseComponent.extend(rf.ui.AutocompleteBase);
 21 
 22     // define super class link
 23     var $super = rf.ui.AutocompleteBase.$super;
 24 
 25     var defaultOptions = {
 26         changeDelay:8
 27     };
 28 
 29     var bindEventHandlers = function() {
 30 
 31         var inputEventHandlers = {};
 32 
 33         if (this.options.buttonId) {
 34             inputEventHandlers["mousedown" + this.namespace] = onButtonShow;
 35             inputEventHandlers["mouseup" + this.namespace] = onSelectMouseUp;
 36             rf.Event.bindById(this.options.buttonId, inputEventHandlers, this);
 37         }
 38 
 39         inputEventHandlers = {};
 40         inputEventHandlers["focus" + this.namespace] = onFocus;
 41         inputEventHandlers["blur" + this.namespace] = onBlur;
 42         inputEventHandlers["click" + this.namespace] = onClick;
 43         inputEventHandlers["keydown" + this.namespace] = onKeyDown;
 44         inputEventHandlers["change" + this.namespace] = function (event) {
 45             if (this.focused) {
 46                 event.stopPropagation()
 47             }
 48         };
 49         rf.Event.bindById(this.fieldId, inputEventHandlers, this);
 50 
 51         inputEventHandlers = {};
 52         inputEventHandlers["mousedown" + this.namespace] = onSelectMouseDown;
 53         inputEventHandlers["mouseup" + this.namespace] = onSelectMouseUp;
 54         rf.Event.bindById(this.selectId, inputEventHandlers, this);
 55     };
 56 
 57     var onSelectMouseDown = function () {
 58         this.isMouseDown = true;
 59     };
 60     var onSelectMouseUp = function () {
 61         rf.getDomElement(this.fieldId).focus();
 62     };
 63 
 64     var onButtonShow = function (event) {
 65         this.isMouseDown = true;
 66         if (this.timeoutId) {
 67             window.clearTimeout(this.timeoutId);
 68             this.timeoutId = null;
 69         }
 70 
 71         rf.getDomElement(this.fieldId).focus();
 72         if (this.isVisible) {
 73             this.__hide(event);
 74         } else {
 75             onShow.call(this, event);
 76         }
 77     };
 78 
 79     var onFocus = function (event) {
 80         if (!this.focused) {
 81             this.__focusValue = this.getValue();
 82             this.focused = true;
 83             this.invokeEvent("focus", rf.getDomElement(this.fieldId), event);
 84         }
 85     };
 86 
 87     var onBlur = function (event) {
 88         if (this.isMouseDown) {
 89             rf.getDomElement(this.fieldId).focus();
 90             this.isMouseDown = false;
 91         } else if (!this.isMouseDown) {
 92             if (this.isVisible) {
 93                 var _this = this;
 94                 this.timeoutId = window.setTimeout(function() {
 95                     if (_this.isVisible) {
 96                         _this.__hide(event);
 97                     }
 98                 }, 200);
 99             }
100             if (this.focused) {
101                 this.focused = false;
102                 this.invokeEvent("blur", rf.getDomElement(this.fieldId), event);
103                 if (this.__focusValue != this.getValue()) {
104                     this.invokeEvent("change", rf.getDomElement(this.fieldId), event);
105                 }
106             }
107         }
108     };
109 
110     var onClick = function (event) {
111     };
112 
113     var onChange = function (event) {
114         if (this.isChanged) {
115             if (this.getValue() == this.tempValue) return;
116         }
117         this.isChanged = false;
118         var value = this.getValue();
119         var flag = value != this.currentValue;
120         //TODO: is it needed to chesk keys?
121         //TODO: we need to set value when autoFill used when LEFT or RIGHT was pressed
122         if (event.keyCode == rf.KEYS.LEFT || event.keyCode == rf.KEYS.RIGHT || flag) {
123             if (flag) {
124                 this.currentValue = this.getValue();
125                 this.__onChangeValue(event, undefined, (!this.isVisible ? this.__show : undefined));
126             } else if (this.isVisible) {
127                 this.__onChangeValue(event);
128             }
129         }
130     };
131 
132     var onShow = function (event) {
133         if (this.isChanged) {
134             this.isChanged = false;
135             onChange.call(this, {});
136         } else {
137             !this.__updateState(event) && this.__show(event);
138         }
139     };
140 
141     var onKeyDown = function (event) {
142         switch (event.keyCode) {
143             case rf.KEYS.UP:
144                 event.preventDefault();
145                 if (this.isVisible) {
146                     this.__onKeyUp(event);
147                 }
148                 break;
149             case rf.KEYS.DOWN:
150                 event.preventDefault();
151                 if (this.isVisible) {
152                     this.__onKeyDown(event);
153                 } else {
154                     onShow.call(this, event);
155                 }
156                 break;
157             case rf.KEYS.PAGEUP:
158                 if (this.isVisible) {
159                     event.preventDefault();
160                     this.__onPageUp(event);
161                 }
162                 break;
163             case rf.KEYS.PAGEDOWN:
164                 if (this.isVisible) {
165                     event.preventDefault();
166                     this.__onPageDown(event);
167                 }
168                 break;
169             case rf.KEYS.HOME:
170                 if (this.isVisible) {
171                     event.preventDefault();
172                     this.__onKeyHome(event);
173                 }
174                 break;
175             case rf.KEYS.END:
176                 if (this.isVisible) {
177                     event.preventDefault();
178                     this.__onKeyEnd(event);
179                 }
180                 break;
181             case rf.KEYS.RETURN:
182                 if (this.isVisible) {
183                     event.preventDefault();
184                     this.__onEnter(event);
185                     //TODO: bind form submit event handler to cancel form submit under the opera
186                     //cancelSubmit = true;
187                     this.__hide(event);
188                     return false;
189                 }
190                 break;
191             case rf.KEYS.ESC:
192                 this.__hide(event);
193                 break;
194             default:
195                 if (!this.options.selectOnly) {
196                     var _this = this;
197                     window.clearTimeout(this.changeTimerId);
198                     this.changeTimerId = window.setTimeout(function() {
199                         onChange.call(_this, event);
200                     }, this.options.changeDelay)
201                 }
202                 break;
203         }
204     };
205 
206     /*
207      * public API functions definition
208      */
209     var show = function (event) {
210         if (!this.isVisible) {
211             if (this.__onBeforeShow(event) != false) {
212                 this.scrollElements = rf.Event.bindScrollEventHandlers(this.selectId, this.__hide, this, this.namespace);
213                 var element = rf.getDomElement(this.selectId);
214                 if (this.options.attachToBody) {
215                     this.parentElement = element.parentNode;
216                     document.body.appendChild(element);
217                 }
218                 $(element).setPosition({id: this.fieldId}, {type:"DROPDOWN"}).show();
219                 this.isVisible = true;
220                 this.__onShow(event);
221             }
222         }
223     };
224     var hide = function (event) {
225         if (this.isVisible) {
226             this.__conceal();
227             this.isVisible = false;
228             this.__onHide(event);
229         }
230     };
231     
232     var conceal = function () {
233         if (this.isVisible) {
234             if (this.scrollElements) {
235                 rf.Event.unbindScrollEventHandlers(this.scrollElements, this);
236                 this.scrollElements = null;
237             }
238             $(rf.getDomElement(this.selectId)).hide();
239             if (this.options.attachToBody && this.parentElement) {
240                 this.parentElement.appendChild(rf.getDomElement(this.selectId));
241                 this.parentElement = null;
242             }
243         }
244     };
245 
246     var updateInputValue = function (value) {
247         if (this.fieldId) {
248             rf.getDomElement(this.fieldId).value = value;
249             return value;
250         } else {
251             return "";
252         }
253     };
254 
255     /*
256      * Prototype definition
257      */
258     $.extend(rf.ui.AutocompleteBase.prototype, (function () {
259         return {
260             /*
261              * public API functions
262              */
263             name:"AutocompleteBase",
264             showPopup: function (event) {
265                 if (!this.focused) {
266                     rf.getDomElement(this.fieldId).focus();
267                 }
268                 onShow.call(this, event);
269             },
270             hidePopup: function (event) {
271                 this.__hide(event)
272             },
273             getNamespace: function () {
274                 return this.namespace;
275             },
276             getValue: function () {
277                 return this.fieldId ? rf.getDomElement(this.fieldId).value : "";
278             },
279             setValue: function (value) {
280                 if (value == this.currentValue) return;
281                 updateInputValue.call(this, value);
282                 this.isChanged = true;
283             },
284             /*
285              * Protected methods
286              */
287             __updateInputValue: updateInputValue,
288             __show: show,
289             __hide: hide,
290             __conceal: conceal,
291             /*
292              * abstract protected methods
293              */
294             __onChangeValue: function (event) {
295             },
296             __onKeyUp: function (event) {
297             },
298             __onKeyDown: function (event) {
299             },
300             __onPageUp: function (event) {
301             },
302             __onPageDown: function (event) {
303             },
304             __onKeyHome: function (event) {
305             },
306             __onKeyEnd: function (event) {
307             },
308             __onBeforeShow: function (event) {
309             },
310             __onShow: function (event) {
311             },
312             __onHide: function (event) {
313             },
314             /*
315              * Destructor
316              */
317             destroy: function () {
318                 this.parentNode = null;
319                 if (this.scrollElements) {
320                     rf.Event.unbindScrollEventHandlers(this.scrollElements, this);
321                     this.scrollElements = null;
322                 }
323                 this.options.buttonId && rf.Event.unbindById(this.options.buttonId, this.namespace);
324                 rf.Event.unbindById(this.fieldId, this.namespace);
325                 rf.Event.unbindById(this.selectId, this.namespace);
326                 $super.destroy.call(this);
327             }
328         };
329     })());
330 })(RichFaces.jQuery, RichFaces);
331