1 (function ($, rf) { 2 3 rf.ui = rf.ui || {}; 4 5 rf.ui.Select = function(id, options) { 6 this.id = id; 7 this.element = this.attachToDom(); 8 var mergedOptions = $.extend({}, defaultOptions, options); 9 mergedOptions['attachTo'] = id; 10 mergedOptions['scrollContainer'] = $(document.getElementById(id + "Items")).parent()[0]; 11 mergedOptions['focusKeeperEnabled'] = false; 12 $super.constructor.call(this, id, mergedOptions); 13 14 this.options = mergedOptions; 15 this.defaultLabel = mergedOptions.defaultLabel; 16 var inputLabel = this.__getValue(); 17 this.initialValue = (inputLabel != this.defaultLabel) ? inputLabel : ""; 18 this.selValueInput = $(document.getElementById(id + "selValue")); 19 this.container = this.selValueInput.parent(); 20 this.clientSelectItems = mergedOptions.clientSelectItems; 21 this.filterFunction = mergedOptions.filterFunction; 22 23 24 if (mergedOptions.showControl && !mergedOptions.disabled) { 25 this.container.bind("mousedown", $.proxy(this.__onBtnMouseDown, this)) 26 .bind("mouseup", $.proxy(this.__onMouseUp, this)); 27 } 28 29 this.isFirstAjax = true; 30 this.previousValue = this.__getValue(); 31 this.selectFirst = mergedOptions.selectFirst; 32 this.popupList = new rf.ui.PopupList((id + "List"), this, mergedOptions); 33 this.list = this.popupList.__getList(); 34 this.listElem = $(document.getElementById(id + "List")); 35 36 this.listElem.bind("mousedown", $.proxy(this.__onListMouseDown, this)); 37 this.listElem.bind("mouseup", $.proxy(this.__onMouseUp, this)); 38 39 var listEventHandlers = {}; 40 listEventHandlers["listshow" + this.namespace] = $.proxy(this.__listshowHandler, this); 41 listEventHandlers["listhide" + this.namespace] = $.proxy(this.__listhideHandler, this); 42 listEventHandlers["change" + this.namespace] = $.proxy(this.__onInputChangeHandler, this); 43 rf.Event.bind(this.element, listEventHandlers, this); 44 45 this.originalItems = this.list.__getItems(); // initialize here for non-autocomplete use cases 46 this.enableManualInput = mergedOptions.enableManualInput || mergedOptions.isAutocomplete; 47 48 if (this.enableManualInput) { 49 updateItemsList.call(this, "", this.clientSelectItems); 50 } 51 this.changeDelay = mergedOptions.changeDelay; 52 }; 53 54 rf.ui.InputBase.extend(rf.ui.Select); 55 var $super = rf.ui.Select.$super; 56 57 var defaultOptions = { 58 defaultLabel: "", 59 selectFirst: true, 60 showControl: true, 61 enableManualInput: false, 62 itemCss: "rf-sel-opt", 63 selectItemCss: "rf-sel-sel", 64 listCss: "rf-sel-lst-cord", 65 changeDelay: 8, 66 disabled: false, 67 filterFunction : undefined, 68 isAutocomplete: false, 69 ajaxMode:true, 70 lazyClientMode:false, 71 isCachedAjax:true 72 }; 73 74 var REGEXP_TRIM = /^[\n\s]*(.*)[\n\s]*$/; 75 76 var updateItemsList = function (value, clientSelectItems) { 77 if (!clientSelectItems) { 78 clientSelectItems = []; 79 } 80 if (clientSelectItems.length || (!this.options.isAutocomplete && !this.options.isCachedAjax)) { 81 // do no empty if autocomplete/caching is on 82 this.clientSelectItems = clientSelectItems; 83 } 84 85 this.originalItems = this.list.__updateItemsList(); 86 this.list.__storeClientSelectItems(clientSelectItems); 87 if (this.originalItems.length > 0) { 88 this.cache = new rf.utils.Cache((this.options.ajaxMode ? value : ""), this.originalItems, getData, !this.options.ajaxMode); 89 } 90 91 }; 92 93 var getData = function (nodeList) { 94 var data = []; 95 nodeList.each(function () { 96 data.push($(this).text().replace(REGEXP_TRIM, "$1")); 97 }); 98 return data; 99 } 100 101 $.extend(rf.ui.Select.prototype, ( function () { 102 return{ 103 name : "select", 104 defaultLabelClass : "rf-sel-dflt-lbl", 105 106 __listshowHandler: function(e) { 107 if (this.originalItems.length == 0 && this.isFirstAjax) { 108 this.callAjax(e); 109 } 110 }, 111 112 __listhideHandler: function(e) { 113 }, 114 115 __onInputChangeHandler: function(e) { 116 this.__setValue(this.input.val()); 117 }, 118 119 __onBtnMouseDown: function(e) { 120 if (!this.popupList.isVisible() && !this.options.isAutocomplete) { 121 this.__updateItems(); 122 this.__showPopup(); 123 } else { 124 this.__hidePopup(); 125 } 126 this.isMouseDown = true; 127 }, 128 129 __focusHandler: function(e) { 130 if (!this.focused) { 131 if (this.__getValue() == this.defaultLabel) { 132 this.__setValue(""); 133 } 134 this.focusValue = this.selValueInput.val(); 135 this.focused = true; 136 this.invokeEvent.call(this, "focus", document.getElementById(this.id), e); 137 } 138 }, 139 140 __keydownHandler: function(e) { 141 var code; 142 143 if (e.keyCode) { 144 code = e.keyCode; 145 } else if (e.which) { 146 code = e.which; 147 } 148 149 var visible = this.popupList.isVisible(); 150 151 switch (code) { 152 case rf.KEYS.DOWN: 153 e.preventDefault(); 154 if (!visible) { 155 this.__updateItems(); 156 this.__showPopup(); 157 } else { 158 this.list.__selectNext(); 159 } 160 break; 161 162 case rf.KEYS.UP: 163 e.preventDefault(); 164 if (visible) { 165 this.list.__selectPrev(); 166 } 167 break; 168 169 case rf.KEYS.RETURN: 170 e.preventDefault(); 171 if (visible) { 172 this.list.__selectCurrent(); 173 } 174 return false; 175 break; 176 177 case rf.KEYS.TAB: 178 break; 179 180 case rf.KEYS.ESC: 181 e.preventDefault(); 182 if (visible) { 183 this.__hidePopup(); 184 } 185 break; 186 187 default: 188 var _this = this; 189 window.clearTimeout(this.changeTimerId); 190 this.changeTimerId = window.setTimeout(function() { 191 _this.__onChangeValue(e); 192 }, this.changeDelay); 193 break; 194 } 195 }, 196 197 __onChangeValue: function(e) { 198 var newValue = this.__getValue(); 199 if (newValue === this.previousValue) { 200 return; 201 } 202 this.previousValue = newValue; 203 if (!this.options.isAutocomplete || 204 (this.options.isCachedAjax || !this.options.ajaxMode) && this.cache && this.cache.isCached(newValue)) { 205 this.__updateItems(); 206 if (this.isAutocomplete) { 207 this.originalItems = this.list.__getItems(); 208 } 209 210 if (this.list.__getItems().length != 0) { 211 this.container.removeClass("rf-sel-fld-err"); 212 } else { 213 this.container.addClass("rf-sel-fld-err"); 214 } 215 216 if (!this.popupList.isVisible()) { 217 this.__showPopup(); 218 } 219 } else { 220 if (newValue.length >= this.options.minChars) { 221 if ((this.options.ajaxMode || this.options.lazyClientMode)) { 222 this.callAjax(e); 223 } 224 } else { 225 if (this.options.ajaxMode) { 226 this.clearItems(); 227 this.__hidePopup(); 228 } 229 } 230 } 231 }, 232 233 clearItems: function() { 234 this.list.removeAllItems(); 235 }, 236 237 callAjax: function(event) { 238 var _this = this; 239 var _event = event; 240 var ajaxSuccess = function (event) { 241 updateItemsList.call(_this, _this.__getValue(), event.componentData && event.componentData[_this.id]); 242 243 if (_this.clientSelectItems.length != 0) { 244 _this.__updateItems(); 245 _this.__showPopup(); 246 } else { 247 _this.__hidePopup(); 248 } 249 }; 250 251 var ajaxError = function (event) { 252 _this.__hidePopup(); 253 _this.clearItems(); 254 }; 255 256 this.isFirstAjax = false; 257 //caution: JSF submits inputs with empty names causing "WARNING: Parameters: Invalid chunk ignored." in Tomcat log 258 var params = {}; 259 params[this.id + ".ajax"] = "1"; 260 rf.ajax(this.id, event, {parameters: params, error: ajaxError, complete:ajaxSuccess}); 261 262 }, 263 264 __blurHandler: function(e) { 265 if (!this.isMouseDown) { 266 var that = this; 267 this.timeoutId = window.setTimeout(function() { 268 if (that.input !== null) { 269 that.onblur(e); 270 } 271 }, 200); 272 } else { 273 this.__setInputFocus(); 274 this.isMouseDown = false; 275 } 276 }, 277 278 __onListMouseDown: function(e) { 279 this.isMouseDown = true; 280 }, 281 282 __onMouseUp: function(e) { 283 this.isMouseDown = false; 284 this.__setInputFocus(); 285 }, 286 287 __updateItems: function() { 288 var newValue = this.__getValue(); 289 newValue = (newValue != this.defaultLabel) ? newValue : ""; 290 this.__updateItemsFromCache(newValue); 291 292 if (this.selectFirst) { 293 this.list.__selectByIndex(0); 294 } 295 }, 296 297 __updateItemsFromCache: function(value) { 298 if (this.originalItems.length > 0 && this.enableManualInput || this.isAutocomplete) { 299 var newItems = this.cache.getItems(value, this.filterFunction); 300 var items = $(newItems); 301 this.list.__unselectPrevious(); 302 this.list.__setItems(items); 303 $(document.getElementById(this.id + "Items")).empty().append(items); 304 } 305 }, 306 307 __getClientItemFromCache: function(inputLabel) { 308 var value; 309 var label; 310 if (this.enableManualInput) { 311 var items = this.cache.getItems(inputLabel, this.filterFunction); 312 if (items && items.length > 0) { 313 var first = $(items[0]); 314 $.each(this.clientSelectItems, function() { 315 if (this.id == first.attr("id")) { 316 label = this.label; 317 value = this.value; 318 return false; 319 } 320 }); 321 } else { 322 label = inputLabel; 323 value = ""; 324 } 325 } 326 327 if (label) { 328 return {'label': label, 'value': value}; 329 } 330 }, 331 332 __getClientItem: function(inputLabel) { 333 var value; 334 var label = inputLabel; 335 $.each(this.clientSelectItems, function() { 336 if (label == this.label) { 337 value = this.value; 338 } 339 }); 340 341 if (label && value) { 342 return {'label': label, 'value': value}; 343 } 344 }, 345 346 __showPopup: function() { 347 if (this.originalItems.length > 0) { 348 this.popupList.show(); 349 } 350 this.invokeEvent.call(this, "listshow", document.getElementById(this.id)); 351 }, 352 353 __hidePopup: function() { 354 this.popupList.hide(); 355 this.invokeEvent.call(this, "listhide", document.getElementById(this.id)); 356 }, 357 358 showPopup: function() { 359 if (!this.popupList.isVisible()) { 360 this.__updateItems(); 361 this.__showPopup(); 362 } 363 this.__setInputFocus(); 364 if (!this.focused) { 365 if (this.__getValue() == this.defaultLabel) { 366 this.__setValue(""); 367 } 368 this.focusValue = this.selValueInput.val(); 369 this.focused = true; 370 this.invokeEvent.call(this, "focus", document.getElementById(this.id)); 371 } 372 }, 373 374 hidePopup: function() { 375 if (this.popupList.isVisible()) { 376 this.__hidePopup(); 377 var inputLabel = this.__getValue(); 378 379 if (!inputLabel || inputLabel == "") { 380 this.__setValue(this.defaultLabel); 381 this.selValueInput.val(""); 382 } 383 384 this.focused = false; 385 this.invokeEvent.call(this, "blur", document.getElementById(this.id)); 386 if (this.focusValue != this.selValueInput.val()) { 387 this.invokeEvent.call(this, "change", document.getElementById(this.id)); 388 } 389 } 390 }, 391 392 processItem: function(item) { 393 var key = $(item).attr("id"); 394 var label; 395 $.each(this.clientSelectItems, function() { 396 if (this.id == key) { 397 label = this.label; 398 return false; 399 } 400 }); 401 this.__setValue(label); 402 this.__hidePopup(); 403 this.__setInputFocus(); 404 this.__save(); 405 406 this.invokeEvent.call(this, "selectitem", document.getElementById(this.id)); 407 }, 408 409 __save: function() { 410 var value = ""; 411 var label = ""; 412 var inputLabel = this.__getValue(); 413 var clientSelectItem; 414 415 if (inputLabel && inputLabel != "") { 416 if (this.enableManualInput) { 417 clientSelectItem = this.__getClientItemFromCache(inputLabel); 418 } else { 419 clientSelectItem = this.__getClientItem(inputLabel); 420 } 421 422 if (clientSelectItem) { 423 label = clientSelectItem.label; 424 value = clientSelectItem.value; 425 } 426 } 427 428 this.__setValue(label); 429 this.selValueInput.val(value); 430 }, 431 432 onblur: function(e) { 433 this.__hidePopup(); 434 var inputLabel = this.__getValue(); 435 436 if (!inputLabel || inputLabel == "") { 437 this.__setValue(this.defaultLabel); 438 this.selValueInput.val(""); 439 } 440 441 this.focused = false; 442 this.invokeEvent.call(this, "blur", document.getElementById(this.id), e); 443 if (this.focusValue != this.selValueInput.val()) { 444 this.invokeEvent.call(this, "change", document.getElementById(this.id), e); 445 } 446 }, 447 448 getValue: function() { 449 return this.selValueInput.val(); 450 }, 451 452 setValue: function(value) { 453 if (value == null || value == '') { 454 this.__setValue(''); 455 this.__save(); 456 this.__updateItems(); 457 return; 458 } 459 var item; 460 for (var i = 0; i < this.clientSelectItems.length; i++) { 461 item = this.clientSelectItems[i]; 462 if (item.value == value) { 463 this.__setValue(item.label); 464 this.__save(); 465 this.list.__selectByIndex(i); 466 return; 467 } 468 } 469 }, 470 471 getLabel: function() { 472 return this.__getValue(); 473 }, 474 475 destroy: function() { 476 this.popupList.destroy(); 477 this.popupList = null; 478 $super.destroy.call(this); 479 } 480 } 481 })()); 482 483 // client-side validation 484 rf.csv = rf.csv || {}; 485 rf.csv.validateSelectLabelValue = function (input, id, params, msg) { 486 var value = $(document.getElementById(id + 'selValue')).val(); 487 var label = $(document.getElementById(id + 'Input')).val(); 488 489 var defaultLabel = RichFaces.component(id).defaultLabel; 490 491 if (!value && label && (label != defaultLabel)) { 492 throw rf.csv.getMessage(null, 'UISELECTONE_INVALID', [id, ""]); 493 } 494 }; 495 496 })(RichFaces.jQuery, window.RichFaces);