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